Merge remote-tracking branch 'origin/master' into tm_sla_supports_backend and add get_current_elevation
This commit is contained in:
commit
c39698878d
@ -1232,6 +1232,19 @@ void ModelVolume::set_material(t_model_material_id material_id, const ModelMater
|
||||
this->object->get_model()->add_material(material_id, material);
|
||||
}
|
||||
|
||||
// Extract the current extruder ID based on this ModelVolume's config and the parent ModelObject's config.
|
||||
int ModelVolume::extruder_id() const
|
||||
{
|
||||
int extruder_id = -1;
|
||||
if (this->is_model_part()) {
|
||||
const ConfigOption *opt = this->config.option("extruder");
|
||||
if (opt == nullptr)
|
||||
opt = this->object->config.option("extruder");
|
||||
extruder_id = (opt == nullptr) ? 0 : opt->getInt();
|
||||
}
|
||||
return extruder_id;
|
||||
}
|
||||
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
void ModelVolume::center_geometry()
|
||||
{
|
||||
@ -1525,6 +1538,71 @@ Transform3d ModelInstance::get_matrix(bool dont_translate, bool dont_rotate, boo
|
||||
}
|
||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
|
||||
|
||||
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
||||
// ordered in the same order. In that case it is not necessary to kill the background processing.
|
||||
bool model_object_list_equal(const Model &model_old, const Model &model_new)
|
||||
{
|
||||
if (model_old.objects.size() != model_new.objects.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < model_old.objects.size(); ++ i)
|
||||
if (model_old.objects[i]->id() != model_new.objects[i]->id())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test whether the new model is just an extension of the old model (new objects were added
|
||||
// to the end of the original list. In that case it is not necessary to kill the background processing.
|
||||
bool model_object_list_extended(const Model &model_old, const Model &model_new)
|
||||
{
|
||||
if (model_old.objects.size() >= model_new.objects.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < model_old.objects.size(); ++ i)
|
||||
if (model_old.objects[i]->id() != model_new.objects[i]->id())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type)
|
||||
{
|
||||
bool modifiers_differ = false;
|
||||
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_new];
|
||||
if (mv_old.type() != type) {
|
||||
++ i_old;
|
||||
continue;
|
||||
}
|
||||
if (mv_new.type() != type) {
|
||||
++ i_new;
|
||||
continue;
|
||||
}
|
||||
if (mv_old.id() != mv_new.id())
|
||||
return true;
|
||||
//FIXME test for the content of the mesh!
|
||||
|
||||
#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) {
|
||||
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
|
||||
if (mv_old.type() == type)
|
||||
// ModelVolume was deleted.
|
||||
return true;
|
||||
}
|
||||
for (; i_new < model_object_new.volumes.size(); ++ i_new) {
|
||||
const ModelVolume &mv_new = *model_object_new.volumes[i_new];
|
||||
if (mv_new.type() == type)
|
||||
// ModelVolume was added.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
||||
void check_model_ids_validity(const Model &model)
|
||||
|
@ -23,6 +23,7 @@ class ModelMaterial;
|
||||
class ModelObject;
|
||||
class ModelVolume;
|
||||
class Print;
|
||||
class SLAPrint;
|
||||
|
||||
typedef std::string t_model_material_id;
|
||||
typedef std::string t_model_material_attribute;
|
||||
@ -252,6 +253,7 @@ public:
|
||||
|
||||
protected:
|
||||
friend class Print;
|
||||
friend class SLAPrint;
|
||||
// Called by Print::apply() to set the model pointer after making a copy.
|
||||
void set_model(Model *model) { m_model = model; }
|
||||
|
||||
@ -310,6 +312,10 @@ public:
|
||||
void set_material_id(t_model_material_id material_id);
|
||||
ModelMaterial* material() const;
|
||||
void set_material(t_model_material_id material_id, const ModelMaterial &material);
|
||||
// Extract the current extruder ID based on this ModelVolume's config and the parent ModelObject's config.
|
||||
// Extruder ID is only valid for FFF. Returns -1 for SLA or if the extruder ID is not applicable (support volumes).
|
||||
int extruder_id() const;
|
||||
|
||||
// Split this volume, append the result to the object owning this volume.
|
||||
// Return the number of volumes created from this one.
|
||||
// This is useful to assign different materials to different volumes of an object.
|
||||
@ -368,6 +374,7 @@ public:
|
||||
|
||||
protected:
|
||||
friend class Print;
|
||||
friend class SLAPrint;
|
||||
friend class ModelObject;
|
||||
|
||||
explicit ModelVolume(const ModelVolume &rhs) = default;
|
||||
@ -535,6 +542,7 @@ public:
|
||||
|
||||
protected:
|
||||
friend class Print;
|
||||
friend class SLAPrint;
|
||||
friend class ModelObject;
|
||||
|
||||
explicit ModelInstance(const ModelInstance &rhs) = default;
|
||||
@ -652,6 +660,18 @@ private:
|
||||
#undef MODELBASE_DERIVED_COPY_MOVE_CLONE
|
||||
#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE
|
||||
|
||||
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
||||
// ordered in the same order. In that case it is not necessary to kill the background processing.
|
||||
extern bool model_object_list_equal(const Model &model_old, const Model &model_new);
|
||||
|
||||
// Test whether the new model is just an extension of the old model (new objects were added
|
||||
// to the end of the original list. In that case it is not necessary to kill the background processing.
|
||||
extern bool model_object_list_extended(const Model &model_old, const Model &model_new);
|
||||
|
||||
// Test whether the new ModelObject contains a different set of volumes (or sorted in a different order)
|
||||
// than the old ModelObject.
|
||||
extern bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type);
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
|
||||
void check_model_ids_validity(const Model &model);
|
||||
|
@ -550,71 +550,6 @@ exit_for_rearrange_regions:
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
|
||||
// ordered in the same order. In that case it is not necessary to kill the background processing.
|
||||
static inline bool model_object_list_equal(const Model &model_old, const Model &model_new)
|
||||
{
|
||||
if (model_old.objects.size() != model_new.objects.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < model_old.objects.size(); ++ i)
|
||||
if (model_old.objects[i]->id() != model_new.objects[i]->id())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test whether the new model is just an extension of the old model (new objects were added
|
||||
// to the end of the original list. In that case it is not necessary to kill the background processing.
|
||||
static inline bool model_object_list_extended(const Model &model_old, const Model &model_new)
|
||||
{
|
||||
if (model_old.objects.size() >= model_new.objects.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < model_old.objects.size(); ++ i)
|
||||
if (model_old.objects[i]->id() != model_new.objects[i]->id())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolume::Type type)
|
||||
{
|
||||
bool modifiers_differ = false;
|
||||
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_new];
|
||||
if (mv_old.type() != type) {
|
||||
++ i_old;
|
||||
continue;
|
||||
}
|
||||
if (mv_new.type() != type) {
|
||||
++ i_new;
|
||||
continue;
|
||||
}
|
||||
if (mv_old.id() != mv_new.id())
|
||||
return true;
|
||||
//FIXME test for the content of the mesh!
|
||||
|
||||
#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) {
|
||||
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
|
||||
if (mv_old.type() == type)
|
||||
// ModelVolume was deleted.
|
||||
return true;
|
||||
}
|
||||
for (; i_new < model_object_new.volumes.size(); ++ i_new) {
|
||||
const ModelVolume &mv_new = *model_object_new.volumes[i_new];
|
||||
if (mv_new.type() == type)
|
||||
// ModelVolume was added.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
|
||||
// in the exact order and with the same IDs.
|
||||
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
|
||||
|
@ -131,9 +131,6 @@ public:
|
||||
SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
|
||||
void delete_support_layer(int idx);
|
||||
|
||||
// methods for handling state
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
||||
// To be used over the layer_height_profile of both the PrintObject and ModelObject
|
||||
// to initialize the height profile with the height ranges.
|
||||
bool update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const;
|
||||
@ -174,6 +171,8 @@ protected:
|
||||
bool invalidate_step(PrintObjectStep step);
|
||||
// Invalidates all PrintObject and Print steps.
|
||||
bool invalidate_all_steps();
|
||||
// Invalidate steps based on a set of parameters changed.
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
||||
private:
|
||||
void make_perimeters();
|
||||
|
@ -2447,6 +2447,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("support_head_front_radius", coFloat);
|
||||
def->label = L("Support head front radius");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("Radius of the pointing side of the head");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2455,6 +2456,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("support_head_penetration", coFloat);
|
||||
def->label = L("Support head penetration");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("How much the pinhead has to penetrate the model surface");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2463,6 +2465,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("support_head_back_radius", coFloat);
|
||||
def->label = L("Support head back radius");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("Radius of the back side of the 3d arrow");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2471,6 +2474,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("support_head_width", coFloat);
|
||||
def->label = L("Support head width");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("Width from the back sphere center to the front sphere center");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2479,6 +2483,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("support_pillar_radius", coFloat);
|
||||
def->label = L("Support pillar radius");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("Radius in mm of the support pillars");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2487,6 +2492,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("support_base_radius", coFloat);
|
||||
def->label = L("Support base radius");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("Radius in mm of the pillar base");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2495,6 +2501,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("support_base_height", coFloat);
|
||||
def->label = L("Support base height");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("The height of the pillar base cone");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2503,6 +2510,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("support_critical_angle", coFloat);
|
||||
def->label = L("Critical angle");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("The default angle for connecting support sticks and junctions.");
|
||||
def->sidetext = L("°");
|
||||
def->cli = "";
|
||||
@ -2511,6 +2519,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("support_max_bridge_length", coFloat);
|
||||
def->label = L("Max bridge length");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("The max length of a bridge");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2519,6 +2528,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("support_object_elevation", coFloat);
|
||||
def->label = L("Object elevation");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("How much the supports should lift up the supported object.");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2527,6 +2537,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("pad_enable", coBool);
|
||||
def->label = L("Use pad");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("Add a pad underneath the supported model");
|
||||
def->sidetext = L("");
|
||||
def->cli = "";
|
||||
@ -2534,6 +2545,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("pad_wall_thickness", coFloat);
|
||||
def->label = L("Pad wall thickness");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2542,6 +2554,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("pad_wall_height", coFloat);
|
||||
def->label = L("Pad wall height");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2550,6 +2563,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("pad_max_merge_distance", coFloat);
|
||||
def->label = L("Max merge distance");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
@ -2558,6 +2572,7 @@ void PrintConfigDef::init_sla_params()
|
||||
|
||||
def = this->add("pad_edge_radius", coFloat);
|
||||
def->label = L("Pad edge radius");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
|
@ -902,7 +902,7 @@ class SLAPrintObjectConfig : public StaticPrintConfig
|
||||
{
|
||||
STATIC_PRINT_CONFIG_CACHE(SLAPrintObjectConfig)
|
||||
public:
|
||||
ConfigOptionFloat layer_height;
|
||||
ConfigOptionFloat layer_height;
|
||||
|
||||
// Radius in mm of the pointing side of the head.
|
||||
ConfigOptionFloat support_head_front_radius /*= 0.2*/;
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include "SLA/SLABasePool.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
@ -75,53 +77,296 @@ void SLAPrint::clear()
|
||||
m_objects.clear();
|
||||
}
|
||||
|
||||
// Transformation without rotation around Z and without a shift by X and Y.
|
||||
static Transform3d sla_trafo(const ModelObject &model_object)
|
||||
{
|
||||
ModelInstance &model_instance = *model_object.instances.front();
|
||||
Vec3d offset = model_instance.get_offset();
|
||||
Vec3d rotation = model_instance.get_rotation();
|
||||
offset(0) = 0.;
|
||||
offset(1) = 0.;
|
||||
rotation(2) = 0.;
|
||||
return Geometry::assemble_transform(offset, rotation, model_instance.get_scaling_factor(), model_instance.get_mirror());
|
||||
}
|
||||
|
||||
// List of instances, where the ModelInstance transformation is a composite of sla_trafo and the transformation defined by SLAPrintObject::Instance.
|
||||
static std::vector<SLAPrintObject::Instance> sla_instances(const ModelObject &model_object)
|
||||
{
|
||||
std::vector<SLAPrintObject::Instance> instances;
|
||||
for (ModelInstance *model_instance : model_object.instances)
|
||||
if (model_instance->is_printable()) {
|
||||
instances.emplace_back(SLAPrintObject::Instance(
|
||||
model_instance->id(),
|
||||
Point::new_scale(model_instance->get_offset(X), model_instance->get_offset(Y)),
|
||||
float(model_instance->get_rotation(Z))));
|
||||
}
|
||||
return instances;
|
||||
}
|
||||
|
||||
SLAPrint::ApplyStatus SLAPrint::apply(const Model &model,
|
||||
const DynamicPrintConfig &config_in)
|
||||
{
|
||||
// if (m_objects.empty())
|
||||
// return APPLY_STATUS_UNCHANGED;
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_validity(model);
|
||||
#endif /* _DEBUG */
|
||||
|
||||
// Make a copy of the config, normalize it.
|
||||
DynamicPrintConfig config(config_in);
|
||||
config.normalize();
|
||||
// Collect changes to print config.
|
||||
t_config_option_keys printer_diff = m_printer_config.diff(config);
|
||||
t_config_option_keys material_diff = m_material_config.diff(config);
|
||||
t_config_option_keys object_diff = m_default_object_config.diff(config);
|
||||
|
||||
// Do not use the ApplyStatus as we will use the max function when updating apply_status.
|
||||
unsigned int apply_status = APPLY_STATUS_UNCHANGED;
|
||||
auto update_apply_status = [&apply_status](bool invalidated)
|
||||
{ apply_status = std::max<unsigned int>(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); };
|
||||
if (! (printer_diff.empty() && material_diff.empty() && object_diff.empty()))
|
||||
update_apply_status(false);
|
||||
|
||||
// Grab the lock for the Print / PrintObject milestones.
|
||||
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||
if (m_objects.empty() && model.objects.empty() && m_model.objects.empty())
|
||||
return APPLY_STATUS_UNCHANGED;
|
||||
|
||||
// Temporary: just to have to correct layer height for the rasterization
|
||||
DynamicPrintConfig config(config_in);
|
||||
config.normalize();
|
||||
m_material_config.initial_layer_height.set(
|
||||
config.opt<ConfigOptionFloat>("initial_layer_height"));
|
||||
// The following call may stop the background processing.
|
||||
update_apply_status(this->invalidate_state_by_config_options(printer_diff));
|
||||
update_apply_status(this->invalidate_state_by_config_options(material_diff));
|
||||
|
||||
// Temporary quick fix, just invalidate everything.
|
||||
{
|
||||
for (SLAPrintObject *print_object : m_objects) {
|
||||
print_object->invalidate_all_steps();
|
||||
delete print_object;
|
||||
}
|
||||
m_objects.clear();
|
||||
this->invalidate_all_steps();
|
||||
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
|
||||
m_printer_config.apply_only(config, printer_diff, true);
|
||||
// Handle changes to material config.
|
||||
m_material_config.apply_only(config, material_diff, true);
|
||||
// Handle changes to object config defaults
|
||||
m_default_object_config.apply_only(config, object_diff, true);
|
||||
|
||||
// Copy the model by value (deep copy),
|
||||
// keep the Model / ModelObject / ModelInstance / ModelVolume IDs.
|
||||
struct ModelObjectStatus {
|
||||
enum Status {
|
||||
Unknown,
|
||||
Old,
|
||||
New,
|
||||
Moved,
|
||||
Deleted,
|
||||
};
|
||||
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
|
||||
ModelID id;
|
||||
Status status;
|
||||
// Search by id.
|
||||
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
|
||||
};
|
||||
std::set<ModelObjectStatus> model_object_status;
|
||||
|
||||
// 1) Synchronize model objects.
|
||||
if (model.id() != m_model.id()) {
|
||||
// Kill everything, initialize from scratch.
|
||||
// Stop background processing.
|
||||
this->call_cancell_callback();
|
||||
update_apply_status(this->invalidate_all_steps());
|
||||
for (SLAPrintObject *object : m_objects) {
|
||||
model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted);
|
||||
delete object;
|
||||
}
|
||||
m_objects.clear();
|
||||
m_model.assign_copy(model);
|
||||
// Generate new SLAPrintObjects.
|
||||
for (ModelObject *model_object : m_model.objects) {
|
||||
auto po = new SLAPrintObject(this, model_object);
|
||||
|
||||
// po->m_config.layer_height.set(lh);
|
||||
po->m_config.apply(config, true);
|
||||
|
||||
m_objects.emplace_back(po);
|
||||
for (ModelInstance *oinst : model_object->instances) {
|
||||
Point tr = Point::new_scale(oinst->get_offset()(X),
|
||||
oinst->get_offset()(Y));
|
||||
auto rotZ = float(oinst->get_rotation()(Z));
|
||||
po->m_instances.emplace_back(oinst->id(), tr, rotZ);
|
||||
for (const ModelObject *model_object : m_model.objects)
|
||||
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
|
||||
} else {
|
||||
if (model_object_list_equal(m_model, model)) {
|
||||
// The object list did not change.
|
||||
for (const ModelObject *model_object : m_model.objects)
|
||||
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
||||
} else if (model_object_list_extended(m_model, model)) {
|
||||
// Add new objects. Their volumes and configs will be synchronized later.
|
||||
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||
for (const ModelObject *model_object : m_model.objects)
|
||||
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(ModelObject::new_copy(*model.objects[i]));
|
||||
m_model.objects.back()->set_model(&m_model);
|
||||
}
|
||||
} else {
|
||||
// Reorder the objects, add new objects.
|
||||
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
|
||||
this->call_cancell_callback();
|
||||
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||
// Second create a new list of objects.
|
||||
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
|
||||
m_model.objects.clear();
|
||||
m_model.objects.reserve(model.objects.size());
|
||||
auto by_id_lower = [](const ModelObject *lhs, const ModelObject *rhs){ return lhs->id() < rhs->id(); };
|
||||
std::sort(model_objects_old.begin(), model_objects_old.end(), by_id_lower);
|
||||
for (const ModelObject *mobj : model.objects) {
|
||||
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(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).
|
||||
m_model.objects.emplace_back(*it);
|
||||
model_object_status.emplace(mobj->id(), ModelObjectStatus::Moved);
|
||||
}
|
||||
}
|
||||
bool deleted_any = false;
|
||||
for (ModelObject *&model_object : model_objects_old) {
|
||||
if (model_object_status.find(ModelObjectStatus(model_object->id())) == model_object_status.end()) {
|
||||
model_object_status.emplace(model_object->id(), ModelObjectStatus::Deleted);
|
||||
deleted_any = true;
|
||||
} else
|
||||
// Do not delete this ModelObject instance.
|
||||
model_object = nullptr;
|
||||
}
|
||||
if (deleted_any) {
|
||||
// Delete PrintObjects of the deleted ModelObjects.
|
||||
std::vector<SLAPrintObject*> print_objects_old = std::move(m_objects);
|
||||
m_objects.clear();
|
||||
m_objects.reserve(print_objects_old.size());
|
||||
for (SLAPrintObject *print_object : print_objects_old) {
|
||||
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
|
||||
assert(it_status != model_object_status.end());
|
||||
if (it_status->status == ModelObjectStatus::Deleted) {
|
||||
update_apply_status(print_object->invalidate_all_steps());
|
||||
delete print_object;
|
||||
} else
|
||||
m_objects.emplace_back(print_object);
|
||||
}
|
||||
for (ModelObject *model_object : model_objects_old)
|
||||
delete model_object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return APPLY_STATUS_INVALIDATED;
|
||||
// 2) Map print objects including their transformation matrices.
|
||||
struct PrintObjectStatus {
|
||||
enum Status {
|
||||
Unknown,
|
||||
Deleted,
|
||||
Reused,
|
||||
New
|
||||
};
|
||||
PrintObjectStatus(SLAPrintObject *print_object, Status status = Unknown) :
|
||||
id(print_object->model_object()->id()),
|
||||
print_object(print_object),
|
||||
trafo(print_object->trafo()),
|
||||
status(status) {}
|
||||
PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
|
||||
// ID of the ModelObject & PrintObject
|
||||
ModelID id;
|
||||
// Pointer to the old PrintObject
|
||||
SLAPrintObject *print_object;
|
||||
// Trafo generated with model_object->world_matrix(true)
|
||||
Transform3d trafo;
|
||||
Status status;
|
||||
// Search by id.
|
||||
bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; }
|
||||
};
|
||||
std::multiset<PrintObjectStatus> print_object_status;
|
||||
for (SLAPrintObject *print_object : m_objects)
|
||||
print_object_status.emplace(PrintObjectStatus(print_object));
|
||||
|
||||
// 3) Synchronize ModelObjects & PrintObjects.
|
||||
std::vector<SLAPrintObject*> print_objects_new;
|
||||
print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
|
||||
bool new_objects = false;
|
||||
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
|
||||
ModelObject &model_object = *m_model.objects[idx_model_object];
|
||||
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
||||
assert(it_status != model_object_status.end());
|
||||
assert(it_status->status != ModelObjectStatus::Deleted);
|
||||
if (it_status->status == ModelObjectStatus::New)
|
||||
// PrintObject instances will be added in the next loop.
|
||||
continue;
|
||||
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
||||
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
||||
const ModelObject &model_object_new = *model.objects[idx_model_object];
|
||||
auto it_print_object_status = print_object_status.lower_bound(PrintObjectStatus(model_object.id()));
|
||||
if (it_print_object_status != print_object_status.end() && it_print_object_status->id != model_object.id())
|
||||
it_print_object_status = print_object_status.end();
|
||||
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolume::MODEL_PART);
|
||||
bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() ||
|
||||
(! model_object.instances.empty() && ! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)));
|
||||
if (model_parts_differ || sla_trafo_differs) {
|
||||
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
||||
if (it_print_object_status != print_object_status.end()) {
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_all_steps());
|
||||
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Deleted;
|
||||
}
|
||||
// Copy content of the ModelObject including its ID, do not change the parent.
|
||||
model_object.assign_copy(model_object_new);
|
||||
} else {
|
||||
// Synchronize Object's config.
|
||||
bool object_config_changed = model_object.config != model_object_new.config;
|
||||
if (object_config_changed)
|
||||
model_object.config = model_object_new.config;
|
||||
if (! object_diff.empty() || object_config_changed) {
|
||||
SLAPrintObjectConfig new_config = m_default_object_config;
|
||||
normalize_and_apply_config(new_config, model_object.config);
|
||||
if (it_print_object_status != print_object_status.end()) {
|
||||
t_config_option_keys diff = it_print_object_status->print_object->config().diff(new_config);
|
||||
if (! diff.empty()) {
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_state_by_config_options(diff));
|
||||
it_print_object_status->print_object->config_apply_only(new_config, diff, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (model_object.sla_support_points != model_object_new.sla_support_points) {
|
||||
model_object.sla_support_points = model_object_new.sla_support_points;
|
||||
if (it_print_object_status != print_object_status.end())
|
||||
update_apply_status(it_print_object_status->print_object->invalidate_step(slaposSupportPoints));
|
||||
}
|
||||
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
|
||||
model_object.name = model_object_new.name;
|
||||
model_object.input_file = model_object_new.input_file;
|
||||
model_object.clear_instances();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SLAPrintObject::Instance> new_instances = sla_instances(model_object);
|
||||
if (it_print_object_status != print_object_status.end() && it_print_object_status->status != PrintObjectStatus::Deleted) {
|
||||
// The SLAPrintObject is already there.
|
||||
if (new_instances != it_print_object_status->print_object->instances()) {
|
||||
// Instances changed.
|
||||
it_print_object_status->print_object->set_instances(new_instances);
|
||||
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||
}
|
||||
print_objects_new.emplace_back(it_print_object_status->print_object);
|
||||
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Reused;
|
||||
} else {
|
||||
auto print_object = new SLAPrintObject(this, &model_object);
|
||||
print_object->set_trafo(sla_trafo(model_object));
|
||||
print_object->set_instances(new_instances);
|
||||
print_object->config_apply(config, true);
|
||||
print_objects_new.emplace_back(print_object);
|
||||
new_objects = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_objects != print_objects_new) {
|
||||
this->call_cancell_callback();
|
||||
update_apply_status(this->invalidate_all_steps());
|
||||
m_objects = print_objects_new;
|
||||
// Delete the PrintObjects marked as Unknown or Deleted.
|
||||
bool deleted_objects = false;
|
||||
for (auto &pos : print_object_status)
|
||||
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
|
||||
// update_apply_status(pos.print_object->invalidate_all_steps());
|
||||
delete pos.print_object;
|
||||
deleted_objects = true;
|
||||
}
|
||||
update_apply_status(new_objects);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
check_model_ids_equal(m_model, model);
|
||||
#endif /* _DEBUG */
|
||||
|
||||
return static_cast<ApplyStatus>(apply_status);
|
||||
}
|
||||
|
||||
void SLAPrint::process()
|
||||
@ -418,12 +663,12 @@ void SLAPrint::process()
|
||||
|
||||
// Status indication
|
||||
auto st = ist + unsigned(sd*level_id*slot/levels.size());
|
||||
slck.lock();
|
||||
{ std::lock_guard<SpinMutex> lck(slck);
|
||||
if( st > pst) {
|
||||
set_status(st, PRINT_STEP_LABELS[slapsRasterize]);
|
||||
pst = st;
|
||||
}
|
||||
slck.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
// last minute escape
|
||||
@ -534,6 +779,61 @@ void SLAPrint::process()
|
||||
set_status(100, L("Slicing done"));
|
||||
}
|
||||
|
||||
bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
||||
{
|
||||
if (opt_keys.empty())
|
||||
return false;
|
||||
|
||||
// Cache the plenty of parameters, which influence the final rasterization only,
|
||||
// or they are only notes not influencing the rasterization step.
|
||||
static std::unordered_set<std::string> steps_rasterize = {
|
||||
"exposure_time",
|
||||
"initial_exposure_time",
|
||||
"material_correction_printing",
|
||||
"material_correction_curing",
|
||||
"display_width",
|
||||
"display_height",
|
||||
"display_pixels_x",
|
||||
"display_pixels_y",
|
||||
"printer_correction"
|
||||
};
|
||||
|
||||
static std::unordered_set<std::string> steps_ignore = {
|
||||
"bed_shape",
|
||||
"max_print_height",
|
||||
"printer_technology",
|
||||
};
|
||||
|
||||
std::vector<SLAPrintStep> steps;
|
||||
std::vector<SLAPrintObjectStep> osteps;
|
||||
bool invalidated = false;
|
||||
|
||||
for (const t_config_option_key &opt_key : opt_keys) {
|
||||
if (steps_rasterize.find(opt_key) != steps_rasterize.end()) {
|
||||
// These options only affect the final rasterization, or they are just notes without influence on the output,
|
||||
// so there is nothing to invalidate.
|
||||
steps.emplace_back(slapsRasterize);
|
||||
} else if (steps_ignore.find(opt_key) != steps_ignore.end()) {
|
||||
// These steps have no influence on the output. Just ignore them.
|
||||
} else if (opt_key == "initial_layer_height") {
|
||||
steps.emplace_back(slapsRasterize);
|
||||
osteps.emplace_back(slaposObjectSlice);
|
||||
} else {
|
||||
// All values should be covered.
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
sort_remove_duplicates(steps);
|
||||
for (SLAPrintStep step : steps)
|
||||
invalidated |= this->invalidate_step(step);
|
||||
sort_remove_duplicates(osteps);
|
||||
for (SLAPrintObjectStep ostep : osteps)
|
||||
for (SLAPrintObject *object : m_objects)
|
||||
invalidated |= object->invalidate_step(ostep);
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
||||
Inherited(print, model_object),
|
||||
m_stepmask(slaposCount, true),
|
||||
@ -545,6 +845,75 @@ SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
||||
|
||||
SLAPrintObject::~SLAPrintObject() {}
|
||||
|
||||
// Called by SLAPrint::apply_config().
|
||||
// This method only accepts SLAPrintObjectConfig option keys.
|
||||
bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys)
|
||||
{
|
||||
if (opt_keys.empty())
|
||||
return false;
|
||||
|
||||
std::vector<SLAPrintObjectStep> steps;
|
||||
bool invalidated = false;
|
||||
for (const t_config_option_key &opt_key : opt_keys) {
|
||||
if ( opt_key == "support_head_front_radius"
|
||||
|| opt_key == "support_head_penetration"
|
||||
|| opt_key == "support_head_back_radius"
|
||||
|| opt_key == "support_head_width"
|
||||
|| opt_key == "support_pillar_radius"
|
||||
|| opt_key == "support_base_radius"
|
||||
|| opt_key == "support_base_height"
|
||||
|| opt_key == "support_critical_angle"
|
||||
|| opt_key == "support_max_bridge_length"
|
||||
|| opt_key == "support_object_elevation") {
|
||||
steps.emplace_back(slaposSupportTree);
|
||||
} else if (
|
||||
opt_key == "pad_enable"
|
||||
|| opt_key == "pad_wall_thickness"
|
||||
|| opt_key == "pad_wall_height"
|
||||
|| opt_key == "pad_max_merge_distance"
|
||||
|| opt_key == "pad_edge_radius") {
|
||||
steps.emplace_back(slaposBasePool);
|
||||
} else {
|
||||
// All keys should be covered.
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
sort_remove_duplicates(steps);
|
||||
for (SLAPrintObjectStep step : steps)
|
||||
invalidated |= this->invalidate_step(step);
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step)
|
||||
{
|
||||
bool invalidated = Inherited::invalidate_step(step);
|
||||
// propagate to dependent steps
|
||||
if (step == slaposObjectSlice) {
|
||||
invalidated |= this->invalidate_all_steps();
|
||||
} else if (step == slaposSupportIslands) {
|
||||
invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposBasePool, slaposSliceSupports });
|
||||
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||
} else if (step == slaposSupportPoints) {
|
||||
invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports });
|
||||
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||
} else if (step == slaposSupportTree) {
|
||||
invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports });
|
||||
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||
} else if (step == slaposBasePool) {
|
||||
invalidated |= this->invalidate_step(slaposSliceSupports);
|
||||
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||
} else if (step == slaposSliceSupports) {
|
||||
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||
}
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
bool SLAPrintObject::invalidate_all_steps()
|
||||
{
|
||||
return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
|
||||
}
|
||||
|
||||
double SLAPrintObject::get_elevation() const {
|
||||
double ret = m_config.support_object_elevation.getFloat();
|
||||
|
||||
@ -565,6 +934,18 @@ double SLAPrintObject::get_elevation() const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
double SLAPrintObject::get_current_elevation() const
|
||||
{
|
||||
bool has_supports = is_step_done(slaposSupportTree);
|
||||
bool has_pad = is_step_done(slaposBasePool);
|
||||
if(!has_supports && !has_pad) return 0;
|
||||
else if(has_supports && !has_pad)
|
||||
return m_config.support_object_elevation.getFloat();
|
||||
else return get_elevation();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace { // dummy empty static containers for return values in some methods
|
||||
const std::vector<ExPolygons> EMPTY_SLICES;
|
||||
const TriangleMesh EMPTY_MESH;
|
||||
|
@ -38,16 +38,18 @@ private: // Prevents erroneous use by other classes.
|
||||
using Inherited = _SLAPrintObjectBase;
|
||||
|
||||
public:
|
||||
const Transform3d& trafo() const { return m_trafo; }
|
||||
const SLAPrintObjectConfig& config() const { return m_config; }
|
||||
const Transform3d& trafo() const { return m_trafo; }
|
||||
|
||||
struct Instance {
|
||||
Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
|
||||
bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; }
|
||||
// ID of the corresponding ModelInstance.
|
||||
ModelID instance_id;
|
||||
// Slic3r::Point objects in scaled G-code coordinates
|
||||
Point shift;
|
||||
// Rotation along the Z axis, in radians.
|
||||
float rotation;
|
||||
float rotation;
|
||||
};
|
||||
const std::vector<Instance>& instances() const { return m_instances; }
|
||||
|
||||
@ -72,6 +74,11 @@ public:
|
||||
// as the pad height also needs to be considered.
|
||||
double get_elevation() const;
|
||||
|
||||
// This method returns the needed elevation according to the processing
|
||||
// status. If the supports are not ready, it is zero, if they are and the
|
||||
// pad is not, then without the pad, otherwise the full value is returned.
|
||||
double get_current_elevation() const;
|
||||
|
||||
// Should be obvious
|
||||
const std::vector<ExPolygons>& get_support_slices() const;
|
||||
const std::vector<ExPolygons>& get_model_slices() const;
|
||||
@ -95,12 +102,14 @@ protected:
|
||||
m_transformed_rmesh.invalidate([this, &trafo](){ m_trafo = trafo; });
|
||||
}
|
||||
|
||||
bool set_instances(const std::vector<Instance> &instances);
|
||||
void set_instances(const std::vector<Instance> &instances) { m_instances = instances; }
|
||||
// Invalidates the step, and its depending steps in SLAPrintObject and SLAPrint.
|
||||
bool invalidate_step(SLAPrintObjectStep step);
|
||||
bool invalidate_all_steps();
|
||||
// Invalidate steps based on a set of parameters changed.
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
||||
private:
|
||||
|
||||
// Object specific configuration, pulled from the configuration layer.
|
||||
SLAPrintObjectConfig m_config;
|
||||
// Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
|
||||
@ -143,7 +152,7 @@ public:
|
||||
PrinterTechnology technology() const noexcept { return ptSLA; }
|
||||
|
||||
void clear() override;
|
||||
bool empty() const override { return false; }
|
||||
bool empty() const override { return m_objects.empty(); }
|
||||
ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
|
||||
void process() override;
|
||||
|
||||
@ -156,8 +165,13 @@ private:
|
||||
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
||||
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
||||
|
||||
// Invalidate steps based on a set of parameters changed.
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
||||
SLAPrinterConfig m_printer_config;
|
||||
SLAMaterialConfig m_material_config;
|
||||
SLAPrintObjectConfig m_default_object_config;
|
||||
|
||||
PrintObjects m_objects;
|
||||
std::vector<bool> m_stepmask;
|
||||
SLAPrinterPtr m_printer;
|
||||
|
@ -31,6 +31,8 @@
|
||||
#define ENABLE_NEW_MENU_LAYOUT (1 && ENABLE_1_42_0)
|
||||
// All rotations made using the rotate gizmo are done with respect to the world reference system
|
||||
#define ENABLE_WORLD_ROTATIONS (1 && ENABLE_1_42_0)
|
||||
// Enables shortcut keys for gizmos
|
||||
#define ENABLE_GIZMOS_SHORTCUT (1 && ENABLE_1_42_0)
|
||||
|
||||
#endif // _technologies_h_
|
||||
|
||||
|
@ -201,6 +201,8 @@ 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 };
|
||||
const float GLVolume::SLA_SUPPORT_COLOR[4] = { 0.75f, 0.75f, 0.75f, 1.0f };
|
||||
const float GLVolume::SLA_PAD_COLOR[4] = { 0.0f, 0.2f, 0.0f, 1.0f };
|
||||
|
||||
GLVolume::GLVolume(float r, float g, float b, float a)
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
@ -214,6 +216,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
|
||||
, m_world_matrix_dirty(true)
|
||||
, m_transformed_bounding_box_dirty(true)
|
||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
, m_sla_shift_z(0.0)
|
||||
, m_transformed_convex_hull_bounding_box_dirty(true)
|
||||
, m_convex_hull(nullptr)
|
||||
, m_convex_hull_owned(false)
|
||||
@ -378,7 +381,14 @@ void GLVolume::set_convex_hull(const TriangleMesh *convex_hull, bool owned)
|
||||
m_convex_hull_owned = owned;
|
||||
}
|
||||
|
||||
#if !ENABLE_MODELVOLUME_TRANSFORM
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
Transform3d GLVolume::world_matrix() const
|
||||
{
|
||||
Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix();
|
||||
m.translation()(2) += m_sla_shift_z;
|
||||
return m;
|
||||
}
|
||||
#else
|
||||
const Transform3f& GLVolume::world_matrix() const
|
||||
{
|
||||
if (m_world_matrix_dirty)
|
||||
@ -388,7 +398,7 @@ const Transform3f& GLVolume::world_matrix() const
|
||||
}
|
||||
return m_world_matrix;
|
||||
}
|
||||
#endif // !ENABLE_MODELVOLUME_TRANSFORM
|
||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
|
||||
const BoundingBoxf3& GLVolume::transformed_bounding_box() const
|
||||
{
|
||||
@ -745,18 +755,9 @@ int GLVolumeCollection::load_object_volume(
|
||||
{ 0.5f, 0.5f, 1.0f, 1.f }
|
||||
};
|
||||
|
||||
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
||||
|
||||
int extruder_id = -1;
|
||||
if (model_volume->is_model_part())
|
||||
{
|
||||
const ConfigOption *opt = model_volume->config.option("extruder");
|
||||
if (opt == nullptr)
|
||||
opt = model_object->config.option("extruder");
|
||||
extruder_id = (opt == nullptr) ? 0 : opt->getInt();
|
||||
}
|
||||
|
||||
const ModelInstance *instance = model_object->instances[instance_idx];
|
||||
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
||||
const int extruder_id = model_volume->extruder_id();
|
||||
const ModelInstance *instance = model_object->instances[instance_idx];
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
const TriangleMesh& mesh = model_volume->mesh;
|
||||
#else
|
||||
@ -822,21 +823,17 @@ void GLVolumeCollection::load_object_auxiliary(
|
||||
bool use_VBOs)
|
||||
{
|
||||
assert(print_object->is_step_done(milestone));
|
||||
Transform3d mesh_trafo_inv = print_object->trafo().inverse();
|
||||
// Get the support mesh.
|
||||
TriangleMesh mesh;
|
||||
switch (milestone) {
|
||||
case slaposSupportTree: mesh = print_object->support_mesh(); break;
|
||||
case slaposBasePool: mesh = print_object->pad_mesh(); break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
TriangleMesh mesh = print_object->get_mesh(milestone);
|
||||
mesh.transform(mesh_trafo_inv);
|
||||
// Convex hull is required for out of print bed detection.
|
||||
TriangleMesh convex_hull = mesh.convex_hull_3d();
|
||||
convex_hull.transform(mesh_trafo_inv);
|
||||
for (const std::pair<size_t, size_t> &instance_idx : instances) {
|
||||
const ModelInstance &model_instance = *print_object->model_object()->instances[instance_idx.first];
|
||||
const SLAPrintObject::Instance &print_instance = print_object->instances()[instance_idx.second];
|
||||
float color[4] { 0.f, 0.f, 1.f, 1.f };
|
||||
this->volumes.emplace_back(new GLVolume(color));
|
||||
this->volumes.emplace_back(new GLVolume((milestone == slaposBasePool) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
|
||||
GLVolume &v = *this->volumes.back();
|
||||
if (use_VBOs)
|
||||
v.indexed_vertex_array.load_mesh_full_shading(mesh);
|
||||
@ -845,13 +842,12 @@ void GLVolumeCollection::load_object_auxiliary(
|
||||
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
|
||||
v.bounding_box = v.indexed_vertex_array.bounding_box();
|
||||
v.indexed_vertex_array.finalize_geometry(use_VBOs);
|
||||
v.composite_id = GLVolume::CompositeID(obj_idx, -1, (int)instance_idx.first);
|
||||
v.composite_id = GLVolume::CompositeID(obj_idx, -milestone, (int)instance_idx.first);
|
||||
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
|
||||
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
|
||||
v.set_convex_hull((&instance_idx == &instances.back()) ? new TriangleMesh(std::move(convex_hull)) : new TriangleMesh(convex_hull), true);
|
||||
v.is_modifier = false;
|
||||
v.shader_outside_printer_detection_enabled = true;
|
||||
//FIXME adjust with print_instance?
|
||||
v.set_instance_transformation(model_instance.get_transformation());
|
||||
// Leave the volume transformation at identity.
|
||||
// v.set_volume_transformation(model_volume->get_transformation());
|
||||
@ -1106,7 +1102,7 @@ void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* con
|
||||
|
||||
for (GLVolume* volume : volumes)
|
||||
{
|
||||
if ((volume == nullptr) || volume->is_modifier || volume->is_wipe_tower)
|
||||
if ((volume == nullptr) || volume->is_modifier || volume->is_wipe_tower || (volume->volume_idx() < 0))
|
||||
continue;
|
||||
|
||||
int extruder_id = volume->extruder_id - 1;
|
||||
|
@ -253,6 +253,8 @@ public:
|
||||
static const float OUTSIDE_COLOR[4];
|
||||
static const float SELECTED_OUTSIDE_COLOR[4];
|
||||
static const float DISABLED_COLOR[4];
|
||||
static const float SLA_SUPPORT_COLOR[4];
|
||||
static const float SLA_PAD_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]) {}
|
||||
@ -276,6 +278,8 @@ private:
|
||||
// Whether or not is needed to recalculate the world matrix.
|
||||
mutable bool m_world_matrix_dirty;
|
||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
// Shift in z required by sla supports+pad
|
||||
double m_sla_shift_z;
|
||||
// Bounding box of this volume, in unscaled coordinates.
|
||||
mutable BoundingBoxf3 m_transformed_bounding_box;
|
||||
// Whether or not is needed to recalculate the transformed bounding box.
|
||||
@ -424,6 +428,9 @@ public:
|
||||
const Vec3d& get_offset() const;
|
||||
void set_offset(const Vec3d& offset);
|
||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
|
||||
double get_sla_shift_z() const { return m_sla_shift_z; }
|
||||
void set_sla_shift_z(double z) { m_sla_shift_z = z; }
|
||||
|
||||
void set_convex_hull(const TriangleMesh *convex_hull, bool owned);
|
||||
|
||||
@ -432,7 +439,7 @@ public:
|
||||
int instance_idx() const { return this->composite_id.instance_id; }
|
||||
|
||||
#if ENABLE_MODELVOLUME_TRANSFORM
|
||||
Transform3d world_matrix() const { return m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); }
|
||||
Transform3d world_matrix() const;
|
||||
#else
|
||||
const Transform3f& world_matrix() const;
|
||||
#endif // ENABLE_MODELVOLUME_TRANSFORM
|
||||
|
@ -1075,7 +1075,6 @@ GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache()
|
||||
, scaling_factor(Vec3d::Ones())
|
||||
, rotation_matrix(Transform3d::Identity())
|
||||
, scale_matrix(Transform3d::Identity())
|
||||
, no_position_matrix(Transform3d::Identity())
|
||||
{
|
||||
}
|
||||
|
||||
@ -1086,7 +1085,6 @@ GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache(const Geometr
|
||||
{
|
||||
rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), rotation);
|
||||
scale_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scaling_factor);
|
||||
no_position_matrix = transform.get_matrix(true);
|
||||
}
|
||||
|
||||
GLCanvas3D::Selection::VolumeCache::VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform)
|
||||
@ -1456,7 +1454,7 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement)
|
||||
(*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement);
|
||||
else if (m_mode == Volume)
|
||||
{
|
||||
Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_no_position_matrix() * m_cache.volumes_data[i].get_volume_no_position_matrix()).inverse() * displacement;
|
||||
Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_volume_rotation_matrix()).inverse() * displacement;
|
||||
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
|
||||
}
|
||||
#else
|
||||
@ -1940,7 +1938,14 @@ void GLCanvas3D::Selection::_update_type()
|
||||
if (m_cache.content.size() == 1) // single object
|
||||
{
|
||||
const ModelObject* model_object = m_model->objects[m_cache.content.begin()->first];
|
||||
unsigned int volumes_count = (unsigned int)model_object->volumes.size();
|
||||
unsigned int model_volumes_count = (unsigned int)model_object->volumes.size();
|
||||
unsigned int sla_volumes_count = 0;
|
||||
for (unsigned int i : m_list)
|
||||
{
|
||||
if ((*m_volumes)[i]->volume_idx() < 0)
|
||||
++sla_volumes_count;
|
||||
}
|
||||
unsigned int volumes_count = model_volumes_count + sla_volumes_count;
|
||||
unsigned int instances_count = (unsigned int)model_object->instances.size();
|
||||
unsigned int selected_instances_count = (unsigned int)m_cache.content.begin()->second.size();
|
||||
if (volumes_count * instances_count == (unsigned int)m_list.size())
|
||||
@ -2659,6 +2664,43 @@ bool GLCanvas3D::Gizmos::is_running() const
|
||||
return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false;
|
||||
}
|
||||
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
bool GLCanvas3D::Gizmos::handle_shortcut(int key, const Selection& selection)
|
||||
{
|
||||
if (!m_enabled)
|
||||
return false;
|
||||
|
||||
bool handled = false;
|
||||
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
|
||||
{
|
||||
if ((it->second == nullptr) || !it->second->is_selectable())
|
||||
continue;
|
||||
|
||||
int it_key = it->second->get_shortcut_key();
|
||||
|
||||
if (it->second->is_activable(selection) && ((it_key == key - 64) || (it_key == key - 96)))
|
||||
{
|
||||
if ((it->second->get_state() == GLGizmoBase::On))
|
||||
{
|
||||
it->second->set_state(GLGizmoBase::Off);
|
||||
m_current = Undefined;
|
||||
handled = true;
|
||||
}
|
||||
else if ((it->second->get_state() == GLGizmoBase::Off))
|
||||
{
|
||||
it->second->set_state(GLGizmoBase::On);
|
||||
m_current = it->first;
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
it->second->set_state(GLGizmoBase::Off);
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
|
||||
bool GLCanvas3D::Gizmos::is_dragging() const
|
||||
{
|
||||
if (!m_enabled)
|
||||
@ -3800,12 +3842,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
|
||||
struct ModelVolumeState {
|
||||
ModelVolumeState(const GLVolume *volume) :
|
||||
geometry_id(volume->geometry_id), volume_idx(-1) {}
|
||||
ModelVolumeState(const ModelID &volume_id, const ModelID &instance_id, const GLVolume::CompositeID &composite_id) :
|
||||
geometry_id(std::make_pair(volume_id.id, instance_id.id)), composite_id(composite_id), volume_idx(-1) {}
|
||||
model_volume(nullptr), geometry_id(volume->geometry_id), volume_idx(-1) {}
|
||||
ModelVolumeState(const ModelVolume *model_volume, const ModelID &instance_id, const GLVolume::CompositeID &composite_id) :
|
||||
model_volume(model_volume), geometry_id(std::make_pair(model_volume->id().id, instance_id.id)), composite_id(composite_id), volume_idx(-1) {}
|
||||
ModelVolumeState(const ModelID &volume_id, const ModelID &instance_id) :
|
||||
geometry_id(std::make_pair(volume_id.id, instance_id.id)), volume_idx(-1) {}
|
||||
model_volume(nullptr), geometry_id(std::make_pair(volume_id.id, instance_id.id)), volume_idx(-1) {}
|
||||
bool new_geometry() const { return this->volume_idx == size_t(-1); }
|
||||
const ModelVolume *model_volume;
|
||||
// ModelID of ModelVolume + ModelID of ModelInstance
|
||||
// or timestamp of an SLAPrintObjectStep + ModelID of ModelInstance
|
||||
std::pair<size_t, size_t> geometry_id;
|
||||
@ -3844,7 +3887,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
const ModelInstance *model_instance = model_object->instances[instance_idx];
|
||||
for (int volume_idx = 0; volume_idx < (int)model_object->volumes.size(); ++ volume_idx) {
|
||||
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
||||
model_volume_state.emplace_back(model_volume->id(), model_instance->id(), GLVolume::CompositeID(object_idx, volume_idx, instance_idx));
|
||||
model_volume_state.emplace_back(model_volume, model_instance->id(), GLVolume::CompositeID(object_idx, volume_idx, instance_idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3894,9 +3937,16 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
delete volume;
|
||||
} else {
|
||||
// This GLVolume will be reused.
|
||||
volume->set_sla_shift_z(0.0);
|
||||
map_glvolume_old_to_new[volume_id] = glvolumes_new.size();
|
||||
mvs->volume_idx = glvolumes_new.size();
|
||||
glvolumes_new.emplace_back(volume);
|
||||
// Update color of the volume based on the current extruder.
|
||||
if (mvs->model_volume != nullptr) {
|
||||
int extruder_id = mvs->model_volume->extruder_id();
|
||||
if (extruder_id != -1)
|
||||
volume->extruder_id = extruder_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3998,7 +4048,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
instances[istep].emplace_back(std::pair<size_t, size_t>(instance_idx, print_instance_idx));
|
||||
else
|
||||
// Recycling an old GLVolume. Update the Object/Instance indices into the current Model.
|
||||
m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, -1, instance_idx);
|
||||
m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4013,11 +4063,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
{
|
||||
// If any volume has been added
|
||||
// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
|
||||
Vec3d shift_z(0.0, 0.0, print_object->get_elevation());
|
||||
double shift_z = print_object->get_elevation();
|
||||
for (GLVolume* volume : m_volumes.volumes)
|
||||
{
|
||||
if (volume->object_idx() == object_idx)
|
||||
volume->set_instance_offset(volume->get_instance_offset() + shift_z);
|
||||
volume->set_sla_shift_z(shift_z);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4273,7 +4323,16 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||
#endif // ENABLE_MODIFIED_CAMERA_TARGET
|
||||
default:
|
||||
{
|
||||
evt.Skip();
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
if (m_gizmos.handle_shortcut(keyCode, m_selection))
|
||||
{
|
||||
_update_gizmos_data();
|
||||
render();
|
||||
}
|
||||
else
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
evt.Skip();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -4761,16 +4820,14 @@ void GLCanvas3D::on_key_down(wxKeyEvent& evt)
|
||||
else
|
||||
{
|
||||
int key = evt.GetKeyCode();
|
||||
#ifdef __WXOSX__
|
||||
if (key == WXK_BACK)
|
||||
#else
|
||||
if (key == WXK_DELETE)
|
||||
#endif // __WXOSX__
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_REMOVE_OBJECT));
|
||||
else
|
||||
{
|
||||
#ifdef __WXOSX__
|
||||
if (key == WXK_BACK)
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_REMOVE_OBJECT));
|
||||
#endif
|
||||
evt.Skip();
|
||||
}
|
||||
evt.Skip();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -384,7 +384,6 @@ public:
|
||||
Vec3d scaling_factor;
|
||||
Transform3d rotation_matrix;
|
||||
Transform3d scale_matrix;
|
||||
Transform3d no_position_matrix;
|
||||
|
||||
TransformCache();
|
||||
explicit TransformCache(const Geometry::Transformation& transform);
|
||||
@ -415,14 +414,12 @@ public:
|
||||
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 Transform3d& get_volume_no_position_matrix() const { return m_volume.no_position_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; }
|
||||
const Transform3d& get_instance_no_position_matrix() const { return m_instance.no_position_matrix; }
|
||||
#else
|
||||
const Vec3d& get_position() const { return m_position; }
|
||||
const Vec3d& get_rotation() const { return m_rotation; }
|
||||
@ -611,6 +608,9 @@ private:
|
||||
EType get_current_type() const;
|
||||
|
||||
bool is_running() const;
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
bool handle_shortcut(int key, const Selection& selection);
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
|
||||
bool is_dragging() const;
|
||||
void start_dragging(const Selection& selection);
|
||||
|
@ -30,6 +30,10 @@
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
#include <wx/defs.h>
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
|
||||
// TODO: Display tooltips quicker on Linux
|
||||
|
||||
static const float DEFAULT_BASE_COLOR[3] = { 0.625f, 0.625f, 0.625f };
|
||||
@ -161,6 +165,9 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent)
|
||||
: m_parent(parent)
|
||||
, m_group_id(-1)
|
||||
, m_state(Off)
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
, m_shortcut_key(0)
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
, m_hover_id(-1)
|
||||
, m_dragging(false)
|
||||
{
|
||||
@ -639,6 +646,10 @@ bool GLGizmoRotate3D::on_init()
|
||||
if (!m_textures[On].load_from_file(path + "rotate_on.png", false))
|
||||
return false;
|
||||
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
m_shortcut_key = WXK_CONTROL_R;
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -713,6 +724,10 @@ bool GLGizmoScale3D::on_init()
|
||||
m_grabbers[2].angles(0) = half_pi;
|
||||
m_grabbers[3].angles(0) = half_pi;
|
||||
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
m_shortcut_key = WXK_CONTROL_S;
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1057,6 +1072,10 @@ bool GLGizmoMove3D::on_init()
|
||||
m_grabbers.push_back(Grabber());
|
||||
}
|
||||
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
m_shortcut_key = WXK_CONTROL_M;
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1213,6 +1232,10 @@ bool GLGizmoFlatten::on_init()
|
||||
if (!m_textures[On].load_from_file(path + "layflat_on.png", false))
|
||||
return false;
|
||||
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
m_shortcut_key = WXK_CONTROL_F;
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1542,6 +1565,10 @@ bool GLGizmoSlaSupports::on_init()
|
||||
if (!m_textures[On].load_from_file(path + "sla_support_points_on.png", false))
|
||||
return false;
|
||||
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
m_shortcut_key = WXK_CONTROL_L;
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1893,6 +1920,10 @@ bool GLGizmoCut::on_init()
|
||||
|
||||
m_grabbers.emplace_back();
|
||||
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
m_shortcut_key = WXK_CONTROL_C;
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -77,6 +77,9 @@ protected:
|
||||
|
||||
int m_group_id;
|
||||
EState m_state;
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
int m_shortcut_key;
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
// textures are assumed to be square and all with the same size in pixels, no internal check is done
|
||||
GLTexture m_textures[Num_States];
|
||||
int m_hover_id;
|
||||
@ -100,6 +103,11 @@ public:
|
||||
EState get_state() const { return m_state; }
|
||||
void set_state(EState state) { m_state = state; on_set_state(); }
|
||||
|
||||
#if ENABLE_GIZMOS_SHORTCUT
|
||||
int get_shortcut_key() const { return m_shortcut_key; }
|
||||
void set_shortcut_key(int key) { m_shortcut_key = key; }
|
||||
#endif // ENABLE_GIZMOS_SHORTCUT
|
||||
|
||||
bool is_activable(const GLCanvas3D::Selection& selection) const { return on_is_activable(selection); }
|
||||
bool is_selectable() const { return on_is_selectable(); }
|
||||
|
||||
|
@ -25,6 +25,7 @@ ObjectList::ObjectList(wxWindow* parent) :
|
||||
{
|
||||
// Fill CATEGORY_ICON
|
||||
{
|
||||
// ptFFF
|
||||
CATEGORY_ICON[L("Layers and Perimeters")] = wxBitmap(from_u8(var("layers.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Infill")] = wxBitmap(from_u8(var("infill.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Support material")] = wxBitmap(from_u8(var("building.png")), wxBITMAP_TYPE_PNG);
|
||||
@ -34,6 +35,9 @@ ObjectList::ObjectList(wxWindow* parent) :
|
||||
// CATEGORY_ICON[L("Skirt and brim")] = wxBitmap(from_u8(var("box.png")), wxBITMAP_TYPE_PNG);
|
||||
// CATEGORY_ICON[L("Speed > Acceleration")] = wxBitmap(from_u8(var("time.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Advanced")] = wxBitmap(from_u8(var("wand.png")), wxBITMAP_TYPE_PNG);
|
||||
// ptSLA
|
||||
CATEGORY_ICON[L("Supports")] = wxBitmap(from_u8(var("building.png")), wxBITMAP_TYPE_PNG);
|
||||
CATEGORY_ICON[L("Pad")] = wxBitmap(from_u8(var("brick.png")), wxBITMAP_TYPE_PNG);
|
||||
}
|
||||
|
||||
// create control
|
||||
@ -310,8 +314,8 @@ void ObjectList::show_context_menu()
|
||||
if (!(m_objects_model->GetItemType(item) & (itObject | itVolume)))
|
||||
return;
|
||||
const auto menu = m_objects_model->GetParent(item) == wxDataViewItem(0) ?
|
||||
create_add_part_popupmenu() :
|
||||
create_part_settings_popupmenu();
|
||||
create_object_popupmenu() :
|
||||
create_part_popupmenu();
|
||||
wxGetApp().tab_panel()->GetPage(0)->PopupMenu(menu);
|
||||
}
|
||||
}
|
||||
@ -436,6 +440,13 @@ void ObjectList::on_drop(wxDataViewEvent &event)
|
||||
|
||||
std::vector<std::string> get_options(const bool is_part)
|
||||
{
|
||||
if (wxGetApp().plater()->printer_technology() == ptSLA) {
|
||||
SLAPrintObjectConfig full_sla_config;
|
||||
auto options = full_sla_config.keys();
|
||||
options.erase(find(options.begin(), options.end(), "layer_height"));
|
||||
return options;
|
||||
}
|
||||
|
||||
PrintRegionConfig reg_config;
|
||||
auto options = reg_config.keys();
|
||||
if (!is_part) {
|
||||
@ -463,7 +474,10 @@ void get_options_menu(settings_menu_hierarchy& settings_menu, bool is_part)
|
||||
if (category.empty() ||
|
||||
(category == "Extruders" && extruders_cnt == 1)) continue;
|
||||
|
||||
std::pair<std::string, std::string> option_label(option, opt->label);
|
||||
const std::string& label = opt->label.empty() ? opt->full_label :
|
||||
opt->full_label.empty() ? opt->label :
|
||||
opt->full_label + " " + opt->label;;
|
||||
std::pair<std::string, std::string> option_label(option, label);
|
||||
std::vector< std::pair<std::string, std::string> > new_category;
|
||||
auto& cat_opt_label = settings_menu.find(category) == settings_menu.end() ? new_category : settings_menu.at(category);
|
||||
cat_opt_label.push_back(option_label);
|
||||
@ -575,14 +589,27 @@ wxMenuItem* ObjectList::menu_item_settings(wxMenu* menu, int id, const bool is_p
|
||||
auto menu_item = new wxMenuItem(menu, id, _(L("Add settings")));
|
||||
menu_item->SetBitmap(m_bmp_cog);
|
||||
|
||||
auto sub_menu = create_add_settings_popupmenu(is_part);
|
||||
auto sub_menu = create_settings_popupmenu(is_part);
|
||||
menu_item->SetSubMenu(sub_menu);
|
||||
return menu_item;
|
||||
}
|
||||
|
||||
wxMenu* ObjectList::create_add_part_popupmenu()
|
||||
wxMenu* ObjectList::create_object_popupmenu()
|
||||
{
|
||||
wxMenu *menu = new wxMenu;
|
||||
if (wxGetApp().plater()->printer_technology() == ptSLA)
|
||||
{
|
||||
wxWindowID config_id_base = NewControlId(1);
|
||||
// Append settings popupmenu
|
||||
menu->Append(menu_item_settings(menu, config_id_base, false));
|
||||
|
||||
menu->Bind(wxEVT_MENU, [menu, this](wxEvent &event) {
|
||||
get_settings_choice(menu, event.GetId(), false);
|
||||
});
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -635,7 +662,7 @@ wxMenu* ObjectList::create_add_part_popupmenu()
|
||||
return menu;
|
||||
}
|
||||
|
||||
wxMenu* ObjectList::create_part_settings_popupmenu()
|
||||
wxMenu* ObjectList::create_part_popupmenu()
|
||||
{
|
||||
wxMenu *menu = new wxMenu;
|
||||
wxWindowID config_id_base = NewControlId(3);
|
||||
@ -671,7 +698,7 @@ wxMenu* ObjectList::create_part_settings_popupmenu()
|
||||
return menu;
|
||||
}
|
||||
|
||||
wxMenu* ObjectList::create_add_settings_popupmenu(bool is_part)
|
||||
wxMenu* ObjectList::create_settings_popupmenu(bool is_part)
|
||||
{
|
||||
wxMenu *menu = new wxMenu;
|
||||
|
||||
|
@ -98,9 +98,9 @@ public:
|
||||
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);
|
||||
wxMenu* create_object_popupmenu();
|
||||
wxMenu* create_part_popupmenu();
|
||||
wxMenu* create_settings_popupmenu(bool is_part);
|
||||
|
||||
void load_subobject(int type);
|
||||
void load_part(ModelObject* model_object, wxArrayString& part_names, int type);
|
||||
|
@ -211,6 +211,11 @@ bool MainFrame::can_delete() const
|
||||
{
|
||||
return (m_plater != nullptr) ? !m_plater->is_selection_empty() : false;
|
||||
}
|
||||
|
||||
bool MainFrame::can_delete_all() const
|
||||
{
|
||||
return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false;
|
||||
}
|
||||
#endif // ENABLE_NEW_MENU_LAYOUT
|
||||
|
||||
void MainFrame::init_menubar()
|
||||
@ -319,11 +324,15 @@ void MainFrame::init_menubar()
|
||||
editMenu = new wxMenu();
|
||||
wxMenuItem* item_select_all = append_menu_item(editMenu, wxID_ANY, L("Select all\tCtrl+A"), L("Selects all objects"),
|
||||
[this](wxCommandEvent&) { m_plater->select_all(); }, "");
|
||||
editMenu->AppendSeparator();
|
||||
wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, L("Delete selected\tDel"), L("Deletes the current selection"),
|
||||
[this](wxCommandEvent&) { m_plater->remove_selected(); }, "");
|
||||
wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, L("Delete all\tCtrl+Del"), L("Deletes all objects"),
|
||||
[this](wxCommandEvent&) { m_plater->reset(); }, "");
|
||||
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_select()); }, item_select_all->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete()); }, item_delete_sel->GetId());
|
||||
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete_all()); }, item_delete_all->GetId());
|
||||
}
|
||||
#endif // ENABLE_NEW_MENU_LAYOUT
|
||||
|
||||
|
@ -73,6 +73,7 @@ class MainFrame : public wxFrame
|
||||
bool can_change_view() const;
|
||||
bool can_select() const;
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
#endif // ENABLE_NEW_MENU_LAYOUT
|
||||
|
||||
public:
|
||||
|
@ -1771,9 +1771,7 @@ unsigned int Plater::priv::update_background_process()
|
||||
}
|
||||
break;
|
||||
case ptSLA:
|
||||
//FIXME as of now the Print::APPLY_STATUS_INVALIDATED is not reliable, and
|
||||
// currently the scene refresh is expensive and loses selection.
|
||||
//return_state |= UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE;
|
||||
return_state |= UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1984,13 +1982,8 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
|
||||
case ptSLA:
|
||||
// Update the SLAPrint from the current Model, so that the reload_scene()
|
||||
// pulls the correct data.
|
||||
|
||||
// FIXME: SLAPrint::apply is not ready for this. At this stage it would
|
||||
// invalidate the previous result and the supports would not be available
|
||||
// for rendering.
|
||||
// if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART)
|
||||
// this->schedule_background_process();
|
||||
|
||||
if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART)
|
||||
this->schedule_background_process();
|
||||
_3DScene::reload_scene(canvas3D, true);
|
||||
break;
|
||||
}
|
||||
@ -2281,6 +2274,8 @@ void Plater::select_view(const std::string& direction) { p->select_view(directio
|
||||
void Plater::select_all() { p->select_all(); }
|
||||
|
||||
void Plater::remove(size_t obj_idx) { p->remove(obj_idx); }
|
||||
void Plater::reset() { p->reset(); }
|
||||
|
||||
void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_model(obj_idx); }
|
||||
|
||||
void Plater::remove_selected()
|
||||
|
@ -130,6 +130,7 @@ public:
|
||||
|
||||
void select_all();
|
||||
void remove(size_t obj_idx);
|
||||
void reset();
|
||||
void delete_object_from_model(size_t obj_idx);
|
||||
void remove_selected();
|
||||
void increase_instances(size_t num = 1);
|
||||
|
@ -3011,6 +3011,7 @@ void TabSLAPrint::build()
|
||||
optgroup->append_single_option_line("support_critical_angle");
|
||||
optgroup->append_single_option_line("support_max_bridge_length");
|
||||
|
||||
page = add_options_page(_(L("Pad")), "brick.png");
|
||||
optgroup = page->new_optgroup(_(L("Pad")));
|
||||
optgroup->append_single_option_line("pad_enable");
|
||||
optgroup->append_single_option_line("pad_wall_thickness");
|
||||
|
Loading…
Reference in New Issue
Block a user