Merge branch 'master' of https://github.com/prusa3d/Slic3r
This commit is contained in:
commit
2ba661cb76
33 changed files with 1890 additions and 1427 deletions
|
@ -14,6 +14,8 @@ public:
|
|||
localmethod_ = m;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void seed(unsigned long val) { nlopt::srand(val); }
|
||||
};
|
||||
|
||||
template<>
|
||||
|
|
|
@ -487,7 +487,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
|||
// starts analyzer calculations
|
||||
if (m_enable_analyzer) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data";
|
||||
m_analyzer.calc_gcode_preview_data(*preview_data);
|
||||
m_analyzer.calc_gcode_preview_data(*preview_data, [print]() { print->throw_if_canceled(); });
|
||||
m_analyzer.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -137,22 +137,22 @@ const std::string& GCodeAnalyzer::process_gcode(const std::string& gcode)
|
|||
return m_process_output;
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::calc_gcode_preview_data(GCodePreviewData& preview_data)
|
||||
void GCodeAnalyzer::calc_gcode_preview_data(GCodePreviewData& preview_data, std::function<void()> cancel_callback)
|
||||
{
|
||||
// resets preview data
|
||||
preview_data.reset();
|
||||
|
||||
// calculates extrusion layers
|
||||
_calc_gcode_preview_extrusion_layers(preview_data);
|
||||
_calc_gcode_preview_extrusion_layers(preview_data, cancel_callback);
|
||||
|
||||
// calculates travel
|
||||
_calc_gcode_preview_travel(preview_data);
|
||||
_calc_gcode_preview_travel(preview_data, cancel_callback);
|
||||
|
||||
// calculates retractions
|
||||
_calc_gcode_preview_retractions(preview_data);
|
||||
_calc_gcode_preview_retractions(preview_data, cancel_callback);
|
||||
|
||||
// calculates unretractions
|
||||
_calc_gcode_preview_unretractions(preview_data);
|
||||
_calc_gcode_preview_unretractions(preview_data, cancel_callback);
|
||||
}
|
||||
|
||||
bool GCodeAnalyzer::is_valid_extrusion_role(ExtrusionRole role)
|
||||
|
@ -676,7 +676,7 @@ bool GCodeAnalyzer::_is_valid_extrusion_role(int value) const
|
|||
return ((int)erNone <= value) && (value <= (int)erMixed);
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& preview_data)
|
||||
void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& preview_data, std::function<void()> cancel_callback)
|
||||
{
|
||||
struct Helper
|
||||
{
|
||||
|
@ -725,9 +725,18 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
|
|||
GCodePreviewData::Range feedrate_range;
|
||||
GCodePreviewData::Range volumetric_rate_range;
|
||||
|
||||
// to avoid to call the callback too often
|
||||
unsigned int cancel_callback_threshold = (unsigned int)extrude_moves->second.size() / 25;
|
||||
unsigned int cancel_callback_curr = 0;
|
||||
|
||||
// constructs the polylines while traversing the moves
|
||||
for (const GCodeMove& move : extrude_moves->second)
|
||||
{
|
||||
// to avoid to call the callback too often
|
||||
cancel_callback_curr = (cancel_callback_curr + 1) % cancel_callback_threshold;
|
||||
if (cancel_callback_curr == 0)
|
||||
cancel_callback();
|
||||
|
||||
if ((data != move.data) || (z != move.start_position.z()) || (position != move.start_position) || (volumetric_rate != move.data.feedrate * (float)move.data.mm3_per_mm))
|
||||
{
|
||||
// store current polyline
|
||||
|
@ -769,7 +778,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
|
|||
preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range);
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
|
||||
void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data, std::function<void()> cancel_callback)
|
||||
{
|
||||
struct Helper
|
||||
{
|
||||
|
@ -797,9 +806,17 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
|
|||
GCodePreviewData::Range width_range;
|
||||
GCodePreviewData::Range feedrate_range;
|
||||
|
||||
// to avoid to call the callback too often
|
||||
unsigned int cancel_callback_threshold = (unsigned int)travel_moves->second.size() / 25;
|
||||
unsigned int cancel_callback_curr = 0;
|
||||
|
||||
// constructs the polylines while traversing the moves
|
||||
for (const GCodeMove& move : travel_moves->second)
|
||||
{
|
||||
cancel_callback_curr = (cancel_callback_curr + 1) % cancel_callback_threshold;
|
||||
if (cancel_callback_curr == 0)
|
||||
cancel_callback();
|
||||
|
||||
GCodePreviewData::Travel::EType move_type = (move.delta_extruder < 0.0f) ? GCodePreviewData::Travel::Retract : ((move.delta_extruder > 0.0f) ? GCodePreviewData::Travel::Extrude : GCodePreviewData::Travel::Move);
|
||||
GCodePreviewData::Travel::Polyline::EDirection move_direction = ((move.start_position.x() != move.end_position.x()) || (move.start_position.y() != move.end_position.y())) ? GCodePreviewData::Travel::Polyline::Generic : GCodePreviewData::Travel::Polyline::Vertical;
|
||||
|
||||
|
@ -840,28 +857,44 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
|
|||
preview_data.ranges.feedrate.update_from(feedrate_range);
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data)
|
||||
void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data, std::function<void()> cancel_callback)
|
||||
{
|
||||
TypeToMovesMap::iterator retraction_moves = m_moves_map.find(GCodeMove::Retract);
|
||||
if (retraction_moves == m_moves_map.end())
|
||||
return;
|
||||
|
||||
// to avoid to call the callback too often
|
||||
unsigned int cancel_callback_threshold = (unsigned int)retraction_moves->second.size() / 25;
|
||||
unsigned int cancel_callback_curr = 0;
|
||||
|
||||
for (const GCodeMove& move : retraction_moves->second)
|
||||
{
|
||||
cancel_callback_curr = (cancel_callback_curr + 1) % cancel_callback_threshold;
|
||||
if (cancel_callback_curr == 0)
|
||||
cancel_callback();
|
||||
|
||||
// store position
|
||||
Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()));
|
||||
preview_data.retraction.positions.emplace_back(position, move.data.width, move.data.height);
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_data)
|
||||
void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_data, std::function<void()> cancel_callback)
|
||||
{
|
||||
TypeToMovesMap::iterator unretraction_moves = m_moves_map.find(GCodeMove::Unretract);
|
||||
if (unretraction_moves == m_moves_map.end())
|
||||
return;
|
||||
|
||||
// to avoid to call the callback too often
|
||||
unsigned int cancel_callback_threshold = (unsigned int)unretraction_moves->second.size() / 25;
|
||||
unsigned int cancel_callback_curr = 0;
|
||||
|
||||
for (const GCodeMove& move : unretraction_moves->second)
|
||||
{
|
||||
cancel_callback_curr = (cancel_callback_curr + 1) % cancel_callback_threshold;
|
||||
if (cancel_callback_curr == 0)
|
||||
cancel_callback();
|
||||
|
||||
// store position
|
||||
Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()));
|
||||
preview_data.unretraction.positions.emplace_back(position, move.data.width, move.data.height);
|
||||
|
|
|
@ -122,7 +122,8 @@ public:
|
|||
const std::string& process_gcode(const std::string& gcode);
|
||||
|
||||
// Calculates all data needed for gcode visualization
|
||||
void calc_gcode_preview_data(GCodePreviewData& preview_data);
|
||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
void calc_gcode_preview_data(GCodePreviewData& preview_data, std::function<void()> cancel_callback = std::function<void()>());
|
||||
|
||||
// Return an estimate of the memory consumed by the time estimator.
|
||||
size_t memory_used() const;
|
||||
|
@ -237,10 +238,11 @@ private:
|
|||
// Checks if the given int is a valid extrusion role (contained into enum ExtrusionRole)
|
||||
bool _is_valid_extrusion_role(int value) const;
|
||||
|
||||
void _calc_gcode_preview_extrusion_layers(GCodePreviewData& preview_data);
|
||||
void _calc_gcode_preview_travel(GCodePreviewData& preview_data);
|
||||
void _calc_gcode_preview_retractions(GCodePreviewData& preview_data);
|
||||
void _calc_gcode_preview_unretractions(GCodePreviewData& preview_data);
|
||||
// All the following methods throw CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
void _calc_gcode_preview_extrusion_layers(GCodePreviewData& preview_data, std::function<void()> cancel_callback);
|
||||
void _calc_gcode_preview_travel(GCodePreviewData& preview_data, std::function<void()> cancel_callback);
|
||||
void _calc_gcode_preview_retractions(GCodePreviewData& preview_data, std::function<void()> cancel_callback);
|
||||
void _calc_gcode_preview_unretractions(GCodePreviewData& preview_data, std::function<void()> cancel_callback);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifdef WIN32
|
||||
|
@ -88,7 +89,7 @@ static DWORD execute_process_winapi(const std::wstring &command_line)
|
|||
// Run the script. If it is a perl script, run it through the bundled perl interpreter.
|
||||
// If it is a batch file, run it through the cmd.exe.
|
||||
// Otherwise run it directly.
|
||||
static int run_script_win32(const std::string &script, const std::string &gcode)
|
||||
static int run_script(const std::string &script, const std::string &gcode, std::string &/*std_err*/)
|
||||
{
|
||||
// Unpack the argument list provided by the user.
|
||||
int nArgs;
|
||||
|
@ -132,9 +133,46 @@ static int run_script_win32(const std::string &script, const std::string &gcode)
|
|||
}
|
||||
|
||||
#else
|
||||
#include <sys/stat.h> //for getting filesystem UID/GID
|
||||
#include <unistd.h> //for getting current UID/GID
|
||||
#include <boost/process.hpp>
|
||||
// POSIX
|
||||
|
||||
#include <cstdlib> // getenv()
|
||||
#include <sstream>
|
||||
#include <boost/process.hpp>
|
||||
|
||||
namespace process = boost::process;
|
||||
|
||||
static int run_script(const std::string &script, const std::string &gcode, std::string &std_err)
|
||||
{
|
||||
// Try to obtain user's default shell
|
||||
const char *shell = ::getenv("SHELL");
|
||||
if (shell == nullptr) { shell = "sh"; }
|
||||
|
||||
// Quote and escape the gcode path argument
|
||||
std::string command { script };
|
||||
command.append(" '");
|
||||
for (char c : gcode) {
|
||||
if (c == '\'') { command.append("'\\''"); }
|
||||
else { command.push_back(c); }
|
||||
}
|
||||
command.push_back('\'');
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Executing script, shell: %1%, command: %2%") % shell % command;
|
||||
|
||||
process::ipstream istd_err;
|
||||
process::child child(shell, "-c", command, process::std_err > istd_err);
|
||||
|
||||
std_err.clear();
|
||||
std::string line;
|
||||
|
||||
while (child.running() && std::getline(istd_err, line)) {
|
||||
std_err.append(line);
|
||||
std_err.push_back('\n');
|
||||
}
|
||||
|
||||
child.wait();
|
||||
return child.exit_code();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -158,27 +196,15 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
|
|||
if (script.empty())
|
||||
continue;
|
||||
BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path;
|
||||
#ifdef WIN32
|
||||
int result = run_script_win32(script, gcode_file.string());
|
||||
#else
|
||||
//FIXME testing existence of a script is risky, as the script line may contain the script and some additional command line parameters.
|
||||
// We would have to process the script line into parameters before testing for the existence of the command, the command may be looked up
|
||||
// in the PATH etc.
|
||||
if (! boost::filesystem::exists(boost::filesystem::path(script)))
|
||||
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + script);
|
||||
struct stat info;
|
||||
if (stat(script.c_str(), &info))
|
||||
throw std::runtime_error(std::string("Cannot read information for post-processing script: ") + script);
|
||||
boost::filesystem::perms script_perms = boost::filesystem::status(script).permissions();
|
||||
//if UID matches, check UID perm. else if GID matches, check GID perm. Otherwise check other perm.
|
||||
if (!(script_perms & ((info.st_uid == geteuid()) ? boost::filesystem::perms::owner_exe
|
||||
: ((info.st_gid == getegid()) ? boost::filesystem::perms::group_exe
|
||||
: boost::filesystem::perms::others_exe))))
|
||||
throw std::runtime_error(std::string("The configured post-processing script is not executable: check permissions. ") + script);
|
||||
int result = boost::process::system(script, gcode_file);
|
||||
if (result < 0)
|
||||
BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned.";
|
||||
#endif
|
||||
|
||||
std::string std_err;
|
||||
const int result = run_script(script, gcode_file.string(), std_err);
|
||||
if (result != 0) {
|
||||
const std::string msg = std_err.empty() ? (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%") % script % path % result).str()
|
||||
: (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%\nOutput:\n%4%") % script % path % result % std_err).str();
|
||||
BOOST_LOG_TRIVIAL(error) << msg;
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -709,12 +709,11 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
|
|||
// The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
|
||||
// Extrude 4 rounds of a brim around the future wipe tower.
|
||||
box_coordinates box(wipeTower_box);
|
||||
box.expand(m_perimeter_width);
|
||||
for (size_t i = 0; i < 4; ++ i) {
|
||||
box.expand(m_perimeter_width - m_layer_height*(1.f-M_PI_4)); // the brim shall have 'normal' spacing with no extra void space
|
||||
writer.travel (box.ld, 7000)
|
||||
.extrude(box.lu, 2100).extrude(box.ru)
|
||||
.extrude(box.rd ).extrude(box.ld);
|
||||
box.expand(m_perimeter_width);
|
||||
}
|
||||
|
||||
writer.travel(wipeTower_box.ld, 7000); // Move to the front left corner.
|
||||
|
|
|
@ -1461,6 +1461,15 @@ int ModelVolume::extruder_id() const
|
|||
return extruder_id;
|
||||
}
|
||||
|
||||
bool ModelVolume::is_splittable() const
|
||||
{
|
||||
// the call mesh.has_multiple_patches() is expensive, so cache the value to calculate it only once
|
||||
if (m_is_splittable == -1)
|
||||
m_is_splittable = (int)mesh.has_multiple_patches();
|
||||
|
||||
return m_is_splittable == 1;
|
||||
}
|
||||
|
||||
void ModelVolume::center_geometry()
|
||||
{
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
|
|
@ -340,8 +340,7 @@ public:
|
|||
// 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;
|
||||
|
||||
void set_splittable(const int val) { m_is_splittable = val; }
|
||||
int is_splittable() const { return m_is_splittable; }
|
||||
bool is_splittable() const;
|
||||
|
||||
// Split this volume, append the result to the object owning this volume.
|
||||
// Return the number of volumes created from this one.
|
||||
|
@ -421,7 +420,7 @@ private:
|
|||
// -1 -> is unknown value (before first cheking)
|
||||
// 0 -> is not splittable
|
||||
// 1 -> is splittable
|
||||
int m_is_splittable {-1};
|
||||
mutable int m_is_splittable{ -1 };
|
||||
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(ModelVolumeType::MODEL_PART), object(object)
|
||||
{
|
||||
|
|
|
@ -2450,6 +2450,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->enum_values.push_back("portrait");
|
||||
def->enum_labels.push_back(L("Landscape"));
|
||||
def->enum_labels.push_back(L("Portrait"));
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionEnum<SLADisplayOrientation>(sladoPortrait);
|
||||
|
||||
def = this->add("fast_tilt_time", coFloat);
|
||||
|
@ -2563,6 +2564,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->category = L("Supports");
|
||||
def->tooltip = L("Generate supports for the models");
|
||||
def->cli = "";
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("support_head_front_diameter", coFloat);
|
||||
|
@ -2572,6 +2574,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(0.4);
|
||||
|
||||
def = this->add("support_head_penetration", coFloat);
|
||||
|
@ -2580,6 +2583,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->tooltip = L("How much the pinhead has to penetrate the model surface");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->mode = comAdvanced;
|
||||
def->min = 0;
|
||||
def->default_value = new ConfigOptionFloat(0.2);
|
||||
|
||||
|
@ -2590,6 +2594,8 @@ void PrintConfigDef::init_sla_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->max = 20;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(1.0);
|
||||
|
||||
def = this->add("support_pillar_diameter", coFloat);
|
||||
|
@ -2599,6 +2605,8 @@ void PrintConfigDef::init_sla_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->max = 15;
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionFloat(1.0);
|
||||
|
||||
def = this->add("support_pillar_connection_mode", coEnum);
|
||||
|
@ -2615,6 +2623,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->enum_labels.push_back(L("Zig-Zag"));
|
||||
def->enum_labels.push_back(L("Cross"));
|
||||
def->enum_labels.push_back(L("Dynamic"));
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionEnum<SLAPillarConnectionMode>(slapcmDynamic);
|
||||
|
||||
def = this->add("support_buildplate_only", coBool);
|
||||
|
@ -2634,6 +2643,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->max = 1;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionFloat(0.0);
|
||||
|
||||
def = this->add("support_base_diameter", coFloat);
|
||||
|
@ -2643,6 +2653,8 @@ void PrintConfigDef::init_sla_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->max = 30;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(4.0);
|
||||
|
||||
def = this->add("support_base_height", coFloat);
|
||||
|
@ -2652,6 +2664,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(1.0);
|
||||
|
||||
def = this->add("support_critical_angle", coFloat);
|
||||
|
@ -2661,6 +2674,8 @@ void PrintConfigDef::init_sla_params()
|
|||
def->sidetext = L("°");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->max = 90;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionFloat(45);
|
||||
|
||||
def = this->add("support_max_bridge_length", coFloat);
|
||||
|
@ -2670,8 +2685,20 @@ void PrintConfigDef::init_sla_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(15.0);
|
||||
|
||||
def = this->add("support_max_pillar_link_distance", coFloat);
|
||||
def->label = L("Max pillar linking distance");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("The max distance of two pillars to get linked with each other."
|
||||
" A zero value will prohibit pillar cascading.");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0; // 0 means no linking
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(10.0);
|
||||
|
||||
def = this->add("support_object_elevation", coFloat);
|
||||
def->label = L("Object elevation");
|
||||
def->category = L("Supports");
|
||||
|
@ -2679,6 +2706,8 @@ void PrintConfigDef::init_sla_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->max = 150; // This is the max height of print on SL1
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(5.0);
|
||||
|
||||
def = this->add("support_points_density_relative", coInt);
|
||||
|
@ -2704,35 +2733,46 @@ void PrintConfigDef::init_sla_params()
|
|||
def->category = L("Pad");
|
||||
def->tooltip = L("Add a pad underneath the supported model");
|
||||
def->cli = "";
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("pad_wall_thickness", coFloat);
|
||||
def->label = L("Pad wall thickness");
|
||||
def->category = L("Pad");
|
||||
// def->tooltip = L("");
|
||||
def->tooltip = L("The thickness of the pad and its optional cavity walls.");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->max = 30;
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionFloat(2.0);
|
||||
|
||||
def = this->add("pad_wall_height", coFloat);
|
||||
def->label = L("Pad wall height");
|
||||
def->tooltip = L("Defines the cavity depth. Set to zero to disable the cavity.");
|
||||
def->category = L("Pad");
|
||||
// def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->max = 30;
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionFloat(5.0);
|
||||
|
||||
def = this->add("pad_max_merge_distance", coFloat);
|
||||
def->label = L("Max merge distance");
|
||||
def->category = L("Pad");
|
||||
// def->tooltip = L("");
|
||||
def->tooltip = L("Some objects can get along with a few smaller pads "
|
||||
"instead of a single big one. This parameter defines "
|
||||
"how far the center of two smaller pads should be. If they"
|
||||
"are closer, they will get merged into one pad.");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionFloat(50.0);
|
||||
|
||||
// This is disabled on the UI. I hope it will never be enabled.
|
||||
def = this->add("pad_edge_radius", coFloat);
|
||||
def->label = L("Pad edge radius");
|
||||
def->category = L("Pad");
|
||||
|
@ -2740,6 +2780,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(1.0);
|
||||
|
||||
def = this->add("pad_wall_slope", coFloat);
|
||||
|
@ -2751,6 +2792,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def->cli = "";
|
||||
def->min = 45;
|
||||
def->max = 90;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(45.0);
|
||||
}
|
||||
|
||||
|
|
|
@ -1008,6 +1008,9 @@ public:
|
|||
// The max length of a bridge in mm
|
||||
ConfigOptionFloat support_max_bridge_length /*= 15.0*/;
|
||||
|
||||
// The max distance of two pillars to get cross linked.
|
||||
ConfigOptionFloat support_max_pillar_link_distance;
|
||||
|
||||
// The elevation in Z direction upwards. This is the space between the pad
|
||||
// and the model object's bounding box bottom. Units in mm.
|
||||
ConfigOptionFloat support_object_elevation /*= 5.0*/;
|
||||
|
@ -1055,6 +1058,7 @@ protected:
|
|||
OPT_PTR(support_base_height);
|
||||
OPT_PTR(support_critical_angle);
|
||||
OPT_PTR(support_max_bridge_length);
|
||||
OPT_PTR(support_max_pillar_link_distance);
|
||||
OPT_PTR(support_points_density_relative);
|
||||
OPT_PTR(support_points_minimal_distance);
|
||||
OPT_PTR(support_object_elevation);
|
||||
|
|
|
@ -496,7 +496,7 @@ void SLAAutoSupports::uniformly_cover(const ExPolygons& islands, Structure& stru
|
|||
poisson_samples.erase(poisson_samples.begin() + poisson_samples_target, poisson_samples.end());
|
||||
}
|
||||
for (const Vec2f &pt : poisson_samples) {
|
||||
m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, 0.2f, is_new_island);
|
||||
m_output.emplace_back(float(pt(0)), float(pt(1)), structure.height, m_config.head_diameter/2.f, is_new_island);
|
||||
structure.supports_force_this_layer += m_config.support_force();
|
||||
grid3d.insert(pt, &structure);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ public:
|
|||
struct Config {
|
||||
float density_relative;
|
||||
float minimal_distance;
|
||||
float head_diameter;
|
||||
///////////////
|
||||
inline float support_force() const { return 10.f / density_relative; } // a force one point can support (arbitrary force unit)
|
||||
inline float tear_pressure() const { return 1.f; } // pressure that the display exerts (the force unit per mm2)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define SLACOMMON_HPP
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
#include <memory>
|
||||
|
||||
// #define SLIC3R_SLA_NEEDS_WINDTREE
|
||||
|
||||
|
@ -44,7 +45,6 @@ struct SupportPoint {
|
|||
bool operator!=(const SupportPoint& sp) const { return !(sp == (*this)); }
|
||||
};
|
||||
|
||||
|
||||
/// An index-triangle structure for libIGL functions. Also serves as an
|
||||
/// alternative (raw) input format for the SLASupportTree
|
||||
/*struct EigenMesh3D {
|
||||
|
@ -78,24 +78,38 @@ public:
|
|||
|
||||
// Result of a raycast
|
||||
class hit_result {
|
||||
double m_t = std::numeric_limits<double>::infinity();
|
||||
double m_t = std::nan("");
|
||||
int m_face_id = -1;
|
||||
const EigenMesh3D& m_mesh;
|
||||
const EigenMesh3D *m_mesh = nullptr;
|
||||
Vec3d m_dir;
|
||||
inline hit_result(const EigenMesh3D& em): m_mesh(em) {}
|
||||
Vec3d m_source;
|
||||
friend class EigenMesh3D;
|
||||
|
||||
// A valid object of this class can only be obtained from
|
||||
// EigenMesh3D::query_ray_hit method.
|
||||
explicit inline hit_result(const EigenMesh3D& em): m_mesh(&em) {}
|
||||
public:
|
||||
|
||||
// This can create a placeholder object which is invalid (not created
|
||||
// by a query_ray_hit call) but the distance can be preset to
|
||||
// a specific value for distinguishing the placeholder.
|
||||
inline hit_result(double val = std::nan("")): m_t(val) {}
|
||||
|
||||
inline double distance() const { return m_t; }
|
||||
inline const Vec3d& direction() const { return m_dir; }
|
||||
inline Vec3d position() const { return m_source + m_dir * m_t; }
|
||||
inline int face() const { return m_face_id; }
|
||||
inline bool is_valid() const { return m_mesh != nullptr; }
|
||||
|
||||
// Hit_result can decay into a double as the hit distance.
|
||||
inline operator double() const { return distance(); }
|
||||
|
||||
inline Vec3d normal() const {
|
||||
if(m_face_id < 0) return {};
|
||||
auto trindex = m_mesh.m_F.row(m_face_id);
|
||||
const Vec3d& p1 = m_mesh.V().row(trindex(0));
|
||||
const Vec3d& p2 = m_mesh.V().row(trindex(1));
|
||||
const Vec3d& p3 = m_mesh.V().row(trindex(2));
|
||||
if(m_face_id < 0 || !is_valid()) return {};
|
||||
auto trindex = m_mesh->m_F.row(m_face_id);
|
||||
const Vec3d& p1 = m_mesh->V().row(trindex(0));
|
||||
const Vec3d& p2 = m_mesh->V().row(trindex(1));
|
||||
const Vec3d& p3 = m_mesh->V().row(trindex(2));
|
||||
Eigen::Vector3d U = p2 - p1;
|
||||
Eigen::Vector3d V = p3 - p1;
|
||||
return U.cross(V).normalized();
|
||||
|
@ -133,6 +147,8 @@ public:
|
|||
|
||||
bool inside(const Vec3d& p) const;
|
||||
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
|
||||
|
||||
double squared_distance(const Vec3d& p, int& i, Vec3d& c) const;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -43,6 +43,8 @@ public:
|
|||
// For testing
|
||||
size_t size() const;
|
||||
bool empty() const { return size() == 0; }
|
||||
|
||||
void foreach(std::function<void(const SpatElement& el)> fn);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -50,11 +50,6 @@ struct SupportConfig {
|
|||
// Width in mm from the back sphere center to the front sphere center.
|
||||
double head_width_mm = 1.0;
|
||||
|
||||
// Radius in mm of the support pillars. The actual radius of the pillars
|
||||
// beginning with a head will not be higher than head_back_radius but the
|
||||
// headless pillars will have half of this value.
|
||||
double headless_pillar_radius_mm = 0.4;
|
||||
|
||||
// How to connect pillars
|
||||
PillarConnectionMode pillar_connection_mode = PillarConnectionMode::dynamic;
|
||||
|
||||
|
@ -74,18 +69,34 @@ struct SupportConfig {
|
|||
double base_height_mm = 1.0;
|
||||
|
||||
// The default angle for connecting support sticks and junctions.
|
||||
double tilt = M_PI/4;
|
||||
double bridge_slope = M_PI/4;
|
||||
|
||||
// The max length of a bridge in mm
|
||||
double max_bridge_length_mm = 15.0;
|
||||
double max_bridge_length_mm = 10.0;
|
||||
|
||||
// The max distance of a pillar to pillar link.
|
||||
double max_pillar_link_distance_mm = 10.0;
|
||||
|
||||
// The elevation in Z direction upwards. This is the space between the pad
|
||||
// and the model object's bounding box bottom.
|
||||
double object_elevation_mm = 10;
|
||||
|
||||
// The max Z angle for a normal at which it will get completely ignored.
|
||||
double normal_cutoff_angle = 150.0 * M_PI / 180.0;
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
// Compile time configuration values (candidates for runtime)
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The max Z angle for a normal at which it will get completely ignored.
|
||||
static const double normal_cutoff_angle;
|
||||
|
||||
// The shortest distance of any support structure from the model surface
|
||||
static const double safety_distance_mm;
|
||||
|
||||
static const double max_solo_pillar_height_mm;
|
||||
static const double max_dual_pillar_height_mm;
|
||||
static const double optimizer_rel_score_diff;
|
||||
static const unsigned optimizer_max_iterations;
|
||||
static const unsigned pillar_cascade_neighbors;
|
||||
static const unsigned max_bridges_on_pillar;
|
||||
};
|
||||
|
||||
struct PoolConfig;
|
||||
|
@ -116,21 +127,14 @@ using PointSet = Eigen::MatrixXd;
|
|||
//EigenMesh3D to_eigenmesh(const ModelObject& model);
|
||||
|
||||
// Simple conversion of 'vector of points' to an Eigen matrix
|
||||
PointSet to_point_set(const std::vector<sla::SupportPoint>&);
|
||||
//PointSet to_point_set(const std::vector<sla::SupportPoint>&);
|
||||
|
||||
|
||||
/* ************************************************************************** */
|
||||
|
||||
/// Just a wrapper to the runtime error to be recognizable in try blocks
|
||||
class SLASupportsStoppedException: public std::runtime_error {
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
SLASupportsStoppedException();
|
||||
};
|
||||
|
||||
/// The class containing mesh data for the generated supports.
|
||||
class SLASupportTree {
|
||||
class Impl;
|
||||
class Impl; // persistent support data
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
Impl& get() { return *m_impl; }
|
||||
|
@ -140,16 +144,25 @@ class SLASupportTree {
|
|||
const SupportConfig&,
|
||||
const Controller&);
|
||||
|
||||
/// Generate the 3D supports for a model intended for SLA print.
|
||||
bool generate(const PointSet& pts,
|
||||
// The generation algorithm is quite long and will be captured in a separate
|
||||
// class with private data, helper methods, etc... This data is only needed
|
||||
// during the calculation whereas the Impl class contains the persistent
|
||||
// data, mostly the meshes.
|
||||
class Algorithm;
|
||||
|
||||
// Generate the 3D supports for a model intended for SLA print. This
|
||||
// will instantiate the Algorithm class and call its appropriate methods
|
||||
// with status indication.
|
||||
bool generate(const std::vector<SupportPoint>& pts,
|
||||
const EigenMesh3D& mesh,
|
||||
const SupportConfig& cfg = {},
|
||||
const Controller& ctl = {});
|
||||
|
||||
public:
|
||||
|
||||
SLASupportTree();
|
||||
|
||||
SLASupportTree(const PointSet& pts,
|
||||
SLASupportTree(const std::vector<SupportPoint>& pts,
|
||||
const EigenMesh3D& em,
|
||||
const SupportConfig& cfg = {},
|
||||
const Controller& ctl = {});
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <igl/remove_duplicate_vertices.h>
|
||||
#include <igl/signed_distance.h>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
#include "SLASpatIndex.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
|
||||
|
@ -89,6 +91,11 @@ size_t SpatIndex::size() const
|
|||
return m_impl->m_store.size();
|
||||
}
|
||||
|
||||
void SpatIndex::foreach(std::function<void (const SpatElement &)> fn)
|
||||
{
|
||||
for(auto& el : m_impl->m_store) fn(el);
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* EigenMesh3D implementation
|
||||
* ****************************************************************************/
|
||||
|
@ -167,6 +174,7 @@ EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const
|
|||
hit_result ret(*this);
|
||||
ret.m_t = double(hit.t);
|
||||
ret.m_dir = dir;
|
||||
ret.m_source = s;
|
||||
if(!std::isinf(hit.t) && !std::isnan(hit.t)) ret.m_face_id = hit.id;
|
||||
|
||||
return ret;
|
||||
|
@ -186,6 +194,15 @@ bool EigenMesh3D::inside(const Vec3d &p) const {
|
|||
}
|
||||
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
|
||||
|
||||
double EigenMesh3D::squared_distance(const Vec3d &p, int& i, Vec3d& c) const {
|
||||
double sqdst = 0;
|
||||
Eigen::Matrix<double, 1, 3> pp = p;
|
||||
Eigen::Matrix<double, 1, 3> cc;
|
||||
sqdst = m_aabb->squared_distance(m_V, m_F, pp, i, cc);
|
||||
c = cc;
|
||||
return sqdst;
|
||||
}
|
||||
|
||||
/* ****************************************************************************
|
||||
* Misc functions
|
||||
* ****************************************************************************/
|
||||
|
@ -208,22 +225,31 @@ template<class Vec> double distance(const Vec& pp1, const Vec& pp2) {
|
|||
PointSet normals(const PointSet& points,
|
||||
const EigenMesh3D& mesh,
|
||||
double eps,
|
||||
std::function<void()> throw_on_cancel)
|
||||
std::function<void()> thr, // throw on cancel
|
||||
const std::vector<unsigned>& pt_indices = {})
|
||||
{
|
||||
if(points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0)
|
||||
return {};
|
||||
|
||||
Eigen::VectorXd dists;
|
||||
Eigen::VectorXi I;
|
||||
PointSet C;
|
||||
std::vector<unsigned> range = pt_indices;
|
||||
if(range.empty()) {
|
||||
range.resize(size_t(points.rows()), 0);
|
||||
std::iota(range.begin(), range.end(), 0);
|
||||
}
|
||||
|
||||
igl::point_mesh_squared_distance( points, mesh.V(), mesh.F(), dists, I, C);
|
||||
PointSet ret(range.size(), 3);
|
||||
|
||||
PointSet ret(I.rows(), 3);
|
||||
for(int i = 0; i < I.rows(); i++) {
|
||||
throw_on_cancel();
|
||||
auto idx = I(i);
|
||||
auto trindex = mesh.F().row(idx);
|
||||
tbb::parallel_for(size_t(0), range.size(),
|
||||
[&ret, &range, &mesh, &points, thr, eps](size_t ridx)
|
||||
{
|
||||
thr();
|
||||
auto eidx = Eigen::Index(range[ridx]);
|
||||
int faceid = 0;
|
||||
Vec3d p;
|
||||
|
||||
mesh.squared_distance(points.row(eidx), faceid, p);
|
||||
|
||||
auto trindex = mesh.F().row(faceid);
|
||||
|
||||
const Vec3d& p1 = mesh.V().row(trindex(0));
|
||||
const Vec3d& p2 = mesh.V().row(trindex(1));
|
||||
|
@ -237,8 +263,6 @@ PointSet normals(const PointSet& points,
|
|||
// of its triangle. The procedure is the same, get the neighbor
|
||||
// triangles and calculate an average normal.
|
||||
|
||||
const Vec3d& p = C.row(i);
|
||||
|
||||
// mark the vertex indices of the edge. ia and ib marks and edge ic
|
||||
// will mark a single vertex.
|
||||
int ia = -1, ib = -1, ic = -1;
|
||||
|
@ -266,7 +290,7 @@ PointSet normals(const PointSet& points,
|
|||
std::vector<Vec3i> neigh;
|
||||
if(ic >= 0) { // The point is right on a vertex of the triangle
|
||||
for(int n = 0; n < mesh.F().rows(); ++n) {
|
||||
throw_on_cancel();
|
||||
thr();
|
||||
Vec3i ni = mesh.F().row(n);
|
||||
if((ni(X) == ic || ni(Y) == ic || ni(Z) == ic))
|
||||
neigh.emplace_back(ni);
|
||||
|
@ -275,7 +299,7 @@ PointSet normals(const PointSet& points,
|
|||
else if(ia >= 0 && ib >= 0) { // the point is on and edge
|
||||
// now get all the neigboring triangles
|
||||
for(int n = 0; n < mesh.F().rows(); ++n) {
|
||||
throw_on_cancel();
|
||||
thr();
|
||||
Vec3i ni = mesh.F().row(n);
|
||||
if((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) &&
|
||||
(ni(X) == ib || ni(Y) == ib || ni(Z) == ib))
|
||||
|
@ -316,52 +340,32 @@ PointSet normals(const PointSet& points,
|
|||
Vec3d sumnorm(0, 0, 0);
|
||||
sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm);
|
||||
sumnorm.normalize();
|
||||
ret.row(i) = sumnorm;
|
||||
ret.row(long(ridx)) = sumnorm;
|
||||
}
|
||||
else { // point lies safely within its triangle
|
||||
Eigen::Vector3d U = p2 - p1;
|
||||
Eigen::Vector3d V = p3 - p1;
|
||||
ret.row(i) = U.cross(V).normalized();
|
||||
}
|
||||
ret.row(long(ridx)) = U.cross(V).normalized();
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
namespace bgi = boost::geometry::index;
|
||||
using Index3D = bgi::rtree< SpatElement, bgi::rstar<16, 4> /* ? */ >;
|
||||
|
||||
// Clustering a set of points by the given criteria
|
||||
ClusteredPoints cluster(
|
||||
const sla::PointSet& points,
|
||||
std::function<bool(const SpatElement&, const SpatElement&)> pred,
|
||||
unsigned max_points = 0)
|
||||
ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
|
||||
std::function<std::vector<SpatElement>(const Index3D&, const SpatElement&)> qfn)
|
||||
{
|
||||
|
||||
namespace bgi = boost::geometry::index;
|
||||
using Index3D = bgi::rtree< SpatElement, bgi::rstar<16, 4> /* ? */ >;
|
||||
|
||||
// A spatial index for querying the nearest points
|
||||
Index3D sindex;
|
||||
|
||||
// Build the index
|
||||
for(unsigned idx = 0; idx < points.rows(); idx++)
|
||||
sindex.insert( std::make_pair(points.row(idx), idx));
|
||||
|
||||
using Elems = std::vector<SpatElement>;
|
||||
|
||||
// Recursive function for visiting all the points in a given distance to
|
||||
// each other
|
||||
std::function<void(Elems&, Elems&)> group =
|
||||
[&sindex, &group, pred, max_points](Elems& pts, Elems& cluster)
|
||||
[&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster)
|
||||
{
|
||||
for(auto& p : pts) {
|
||||
std::vector<SpatElement> tmp;
|
||||
|
||||
sindex.query(
|
||||
bgi::satisfies([p, pred](const SpatElement& se) {
|
||||
return pred(p, se);
|
||||
}),
|
||||
std::back_inserter(tmp)
|
||||
);
|
||||
|
||||
std::vector<SpatElement> tmp = qfn(sindex, p);
|
||||
auto cmp = [](const SpatElement& e1, const SpatElement& e2){
|
||||
return e1.second < e2.second;
|
||||
};
|
||||
|
@ -405,5 +409,84 @@ ClusteredPoints cluster(
|
|||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::vector<SpatElement> distance_queryfn(const Index3D& sindex,
|
||||
const SpatElement& p,
|
||||
double dist,
|
||||
unsigned max_points)
|
||||
{
|
||||
std::vector<SpatElement> tmp; tmp.reserve(max_points);
|
||||
sindex.query(
|
||||
bgi::nearest(p.first, max_points),
|
||||
std::back_inserter(tmp)
|
||||
);
|
||||
|
||||
for(auto it = tmp.begin(); it < tmp.end(); ++it)
|
||||
if(distance(p.first, it->first) > dist) it = tmp.erase(it);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// Clustering a set of points by the given criteria
|
||||
ClusteredPoints cluster(
|
||||
const std::vector<unsigned>& indices,
|
||||
std::function<Vec3d(unsigned)> pointfn,
|
||||
double dist,
|
||||
unsigned max_points)
|
||||
{
|
||||
// A spatial index for querying the nearest points
|
||||
Index3D sindex;
|
||||
|
||||
// Build the index
|
||||
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
|
||||
|
||||
return cluster(sindex, max_points,
|
||||
[dist, max_points](const Index3D& sidx, const SpatElement& p)
|
||||
{
|
||||
return distance_queryfn(sidx, p, dist, max_points);
|
||||
});
|
||||
}
|
||||
|
||||
// Clustering a set of points by the given criteria
|
||||
ClusteredPoints cluster(
|
||||
const std::vector<unsigned>& indices,
|
||||
std::function<Vec3d(unsigned)> pointfn,
|
||||
std::function<bool(const SpatElement&, const SpatElement&)> predicate,
|
||||
unsigned max_points)
|
||||
{
|
||||
// A spatial index for querying the nearest points
|
||||
Index3D sindex;
|
||||
|
||||
// Build the index
|
||||
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
|
||||
|
||||
return cluster(sindex, max_points,
|
||||
[max_points, predicate](const Index3D& sidx, const SpatElement& p)
|
||||
{
|
||||
std::vector<SpatElement> tmp; tmp.reserve(max_points);
|
||||
sidx.query(bgi::satisfies([p, predicate](const SpatElement& e){
|
||||
return predicate(p, e);
|
||||
}), std::back_inserter(tmp));
|
||||
return tmp;
|
||||
});
|
||||
}
|
||||
|
||||
ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points)
|
||||
{
|
||||
// A spatial index for querying the nearest points
|
||||
Index3D sindex;
|
||||
|
||||
// Build the index
|
||||
for(Eigen::Index i = 0; i < pts.rows(); i++)
|
||||
sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i)));
|
||||
|
||||
return cluster(sindex, max_points,
|
||||
[dist, max_points](const Index3D& sidx, const SpatElement& p)
|
||||
{
|
||||
return distance_queryfn(sidx, p, dist, max_points);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -545,9 +545,9 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
|
|||
scfg.head_penetration_mm = c.support_head_penetration.getFloat();
|
||||
scfg.head_width_mm = c.support_head_width.getFloat();
|
||||
scfg.object_elevation_mm = c.support_object_elevation.getFloat();
|
||||
scfg.tilt = c.support_critical_angle.getFloat() * PI / 180.0 ;
|
||||
scfg.bridge_slope = c.support_critical_angle.getFloat() * PI / 180.0 ;
|
||||
scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat();
|
||||
scfg.headless_pillar_radius_mm = 0.375*c.support_pillar_diameter.getFloat();
|
||||
scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.getFloat();
|
||||
switch(c.support_pillar_connection_mode.getInt()) {
|
||||
case slapcmZigZag:
|
||||
scfg.pillar_connection_mode = sla::PillarConnectionMode::zigzag; break;
|
||||
|
@ -674,6 +674,7 @@ void SLAPrint::process()
|
|||
// the density config value is in percents:
|
||||
config.density_relative = float(cfg.support_points_density_relative / 100.f);
|
||||
config.minimal_distance = float(cfg.support_points_minimal_distance);
|
||||
config.head_diameter = float(cfg.support_head_front_diameter);
|
||||
|
||||
// Construction of this object does the calculation.
|
||||
this->throw_if_canceled();
|
||||
|
@ -711,7 +712,6 @@ void SLAPrint::process()
|
|||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
sla::SupportConfig scfg = make_support_cfg(po.m_config);
|
||||
sla::Controller ctl;
|
||||
|
||||
|
@ -737,9 +737,11 @@ void SLAPrint::process()
|
|||
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
||||
|
||||
po.m_supportdata->support_tree_ptr.reset(
|
||||
new SLASupportTree(sla::to_point_set(po.m_supportdata->support_points),
|
||||
new SLASupportTree(po.m_supportdata->support_points,
|
||||
po.m_supportdata->emesh, scfg, ctl));
|
||||
|
||||
throw_if_canceled();
|
||||
|
||||
// Create the unified mesh
|
||||
auto rc = SlicingStatus::RELOAD_SCENE;
|
||||
|
||||
|
@ -755,10 +757,7 @@ void SLAPrint::process()
|
|||
BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty";
|
||||
|
||||
report_status(*this, -1, L("Visualizing supports"), rc);
|
||||
} catch(sla::SLASupportsStoppedException&) {
|
||||
// no need to rethrow
|
||||
// throw_if_canceled();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// This step generates the sla base pad
|
||||
|
@ -1390,6 +1389,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
|
|||
|| opt_key == "support_base_height"
|
||||
|| opt_key == "support_critical_angle"
|
||||
|| opt_key == "support_max_bridge_length"
|
||||
|| opt_key == "support_max_pillar_link_distance"
|
||||
|| opt_key == "support_object_elevation") {
|
||||
steps.emplace_back(slaposSupportTree);
|
||||
} else if (
|
||||
|
|
|
@ -247,6 +247,11 @@ private:
|
|||
// Invalidate steps based on a set of parameters changed.
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
||||
std::vector<float> calculate_heights(const BoundingBoxf3& bb,
|
||||
float elevation,
|
||||
float initial_layer_height,
|
||||
float layer_height) const;
|
||||
|
||||
void fill_statistics();
|
||||
|
||||
SLAPrintConfig m_print_config;
|
||||
|
@ -270,8 +275,6 @@ private:
|
|||
lref(std::cref(lyr)), copies(std::cref(cp)) {}
|
||||
};
|
||||
|
||||
std::vector<float> calculate_heights(const BoundingBoxf3& bb, float elevation, float initial_layer_height, float layer_height) const;
|
||||
|
||||
// One level may contain multiple slices from multiple objects and their
|
||||
// supports
|
||||
using LayerRefs = std::vector<LayerRef>;
|
||||
|
|
|
@ -4564,11 +4564,18 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
|
||||
const Print *print = m_process->fff_print();
|
||||
float depth = print->get_wipe_tower_depth();
|
||||
|
||||
// Calculate wipe tower brim spacing.
|
||||
const DynamicPrintConfig &print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
double layer_height = print_config.opt_float("layer_height");
|
||||
double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height);
|
||||
float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4);
|
||||
|
||||
if (!print->is_step_done(psWipeTower))
|
||||
depth = (900.f/w) * (float)(extruders_count - 1) ;
|
||||
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
|
||||
1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower),
|
||||
print->config().nozzle_diameter.values[0] * 1.25f * 4.5f);
|
||||
brim_spacing * 4.5f);
|
||||
if (volume_idx_wipe_tower_old != -1)
|
||||
map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
|
||||
}
|
||||
|
@ -7852,10 +7859,17 @@ void GLCanvas3D::_load_shells_fff()
|
|||
unsigned int extruders_count = config.nozzle_diameter.size();
|
||||
if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
|
||||
float depth = print->get_wipe_tower_depth();
|
||||
|
||||
// Calculate wipe tower brim spacing.
|
||||
const DynamicPrintConfig &print_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||
double layer_height = print_config.opt_float("layer_height");
|
||||
double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height);
|
||||
float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4);
|
||||
|
||||
if (!print->is_step_done(psWipeTower))
|
||||
depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1) ;
|
||||
m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
|
||||
m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower), print->config().nozzle_diameter.values[0] * 1.25f * 4.5f);
|
||||
m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower), brim_spacing * 4.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1873,8 +1873,8 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b
|
|||
float render_color[3];
|
||||
for (int i = 0; i < (int)m_editing_mode_cache.size(); ++i)
|
||||
{
|
||||
const sla::SupportPoint& support_point = m_editing_mode_cache[i].first;
|
||||
const bool& point_selected = m_editing_mode_cache[i].second;
|
||||
const sla::SupportPoint& support_point = m_editing_mode_cache[i].support_point;
|
||||
const bool& point_selected = m_editing_mode_cache[i].selected;
|
||||
|
||||
// First decide about the color of the point.
|
||||
if (picking) {
|
||||
|
@ -1889,7 +1889,7 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b
|
|||
render_color[2] = 1.0f;
|
||||
}
|
||||
else { // neigher hover nor picking
|
||||
bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].first.is_new_island;
|
||||
bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].support_point.is_new_island;
|
||||
if (m_editing_mode) {
|
||||
render_color[0] = point_selected ? 1.0f : (supports_new_island ? 0.3f : 0.7f);
|
||||
render_color[1] = point_selected ? 0.3f : (supports_new_island ? 0.3f : 0.7f);
|
||||
|
@ -1903,12 +1903,32 @@ void GLGizmoSlaSupports::render_points(const GLCanvas3D::Selection& selection, b
|
|||
float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f};
|
||||
::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive);
|
||||
|
||||
// Now render the sphere. Inverse matrix of the instance scaling is applied so that the
|
||||
// sphere does not scale with the object.
|
||||
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
|
||||
::glPushMatrix();
|
||||
::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2));
|
||||
::glMultMatrixd(instance_scaling_matrix_inverse.data());
|
||||
::gluSphere(m_quadric, m_editing_mode_cache[i].first.head_front_radius * RenderPointScale, 64, 36);
|
||||
|
||||
// Matrices set, we can render the point mark now.
|
||||
// If in editing mode, we'll also render a cone pointing to the sphere.
|
||||
if (m_editing_mode) {
|
||||
if (m_editing_mode_cache[i].normal == Vec3f::Zero())
|
||||
update_cache_entry_normal(i); // in case the normal is not yet cached, find and cache it
|
||||
|
||||
Eigen::Quaterniond q;
|
||||
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_mode_cache[i].normal.cast<double>());
|
||||
Eigen::AngleAxisd aa(q);
|
||||
::glRotated(aa.angle() * (180./M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2));
|
||||
|
||||
const float cone_radius = 0.25f; // mm
|
||||
const float cone_height = 0.75f;
|
||||
::glPushMatrix();
|
||||
::glTranslatef(0.f, 0.f, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale);
|
||||
::gluCylinder(m_quadric, 0.f, cone_radius, cone_height, 36, 1);
|
||||
::glTranslatef(0.f, 0.f, cone_height);
|
||||
::gluDisk(m_quadric, 0.0, cone_radius, 36, 1);
|
||||
::glPopMatrix();
|
||||
}
|
||||
::gluSphere(m_quadric, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale, 64, 36);
|
||||
::glPopMatrix();
|
||||
}
|
||||
|
||||
|
@ -1957,7 +1977,7 @@ void GLGizmoSlaSupports::update_mesh()
|
|||
m_AABB.init(m_V, m_F);
|
||||
}
|
||||
|
||||
Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
||||
std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
||||
{
|
||||
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
|
||||
if (m_V.size() == 0)
|
||||
|
@ -1992,9 +2012,16 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
|
|||
if (!m_AABB.intersect_ray(m_V, m_F, point1.cast<float>(), (point2-point1).cast<float>(), hit))
|
||||
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
|
||||
|
||||
int fid = hit.id;
|
||||
Vec3f bc(1-hit.u-hit.v, hit.u, hit.v);
|
||||
return bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
|
||||
int fid = hit.id; // facet id
|
||||
Vec3f bc(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
|
||||
Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
|
||||
Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
|
||||
|
||||
// Calculate and return both the point and the facet normal.
|
||||
return std::make_pair(
|
||||
bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2)),
|
||||
a.cross(b)
|
||||
);
|
||||
}
|
||||
|
||||
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
|
||||
|
@ -2046,10 +2073,9 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
|
||||
// If there is some selection, don't add new point and deselect everything instead.
|
||||
if (m_selection_empty) {
|
||||
Vec3f new_pos;
|
||||
try {
|
||||
new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case
|
||||
m_editing_mode_cache.emplace_back(std::make_pair(sla::SupportPoint(new_pos, m_new_point_head_diameter/2.f, false), false));
|
||||
std::pair<Vec3f, Vec3f> pos_and_normal = unproject_on_mesh(mouse_position); // don't create anything if this throws
|
||||
m_editing_mode_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second);
|
||||
m_unsaved_changes = true;
|
||||
}
|
||||
catch (...) { // not clicked on object
|
||||
|
@ -2090,7 +2116,7 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
|
||||
// Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh:
|
||||
for (unsigned int i=0; i<m_editing_mode_cache.size(); ++i) {
|
||||
const sla::SupportPoint &support_point = m_editing_mode_cache[i].first;
|
||||
const sla::SupportPoint &support_point = m_editing_mode_cache[i].support_point;
|
||||
Vec3f pos = instance_matrix.cast<float>() * support_point.pos;
|
||||
pos(2) += z_offset;
|
||||
GLdouble out_x, out_y, out_z;
|
||||
|
@ -2166,7 +2192,7 @@ bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
void GLGizmoSlaSupports::delete_selected_points(bool force)
|
||||
{
|
||||
for (unsigned int idx=0; idx<m_editing_mode_cache.size(); ++idx) {
|
||||
if (m_editing_mode_cache[idx].second && (!m_editing_mode_cache[idx].first.is_new_island || !m_lock_unique_islands || force)) {
|
||||
if (m_editing_mode_cache[idx].selected && (!m_editing_mode_cache[idx].support_point.is_new_island || !m_lock_unique_islands || force)) {
|
||||
m_editing_mode_cache.erase(m_editing_mode_cache.begin() + (idx--));
|
||||
m_unsaved_changes = true;
|
||||
}
|
||||
|
@ -2181,14 +2207,15 @@ void GLGizmoSlaSupports::delete_selected_points(bool force)
|
|||
|
||||
void GLGizmoSlaSupports::on_update(const UpdateData& data, const GLCanvas3D::Selection& selection)
|
||||
{
|
||||
if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].first.is_new_island || !m_lock_unique_islands)) {
|
||||
Vec3f new_pos;
|
||||
if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].support_point.is_new_island || !m_lock_unique_islands)) {
|
||||
std::pair<Vec3f, Vec3f> pos_and_normal;
|
||||
try {
|
||||
new_pos = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1)));
|
||||
pos_and_normal = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1)));
|
||||
}
|
||||
catch (...) { return; }
|
||||
m_editing_mode_cache[m_hover_id].first.pos = new_pos;
|
||||
m_editing_mode_cache[m_hover_id].first.is_new_island = false;
|
||||
m_editing_mode_cache[m_hover_id].support_point.pos = pos_and_normal.first;
|
||||
m_editing_mode_cache[m_hover_id].support_point.is_new_island = false;
|
||||
m_editing_mode_cache[m_hover_id].normal = pos_and_normal.second;
|
||||
m_unsaved_changes = true;
|
||||
// Do not update immediately, wait until the mouse is released.
|
||||
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
|
@ -2227,15 +2254,15 @@ void GLGizmoSlaSupports::render_tooltip_texture() const {
|
|||
#endif // not ENABLE_IMGUI
|
||||
|
||||
|
||||
std::vector<ConfigOption*> GLGizmoSlaSupports::get_config_options(const std::vector<std::string>& keys) const
|
||||
std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const std::vector<std::string>& keys) const
|
||||
{
|
||||
std::vector<ConfigOption*> out;
|
||||
std::vector<const ConfigOption*> out;
|
||||
|
||||
if (!m_model_object)
|
||||
return out;
|
||||
|
||||
DynamicPrintConfig& object_cfg = m_model_object->config;
|
||||
DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
const DynamicPrintConfig& object_cfg = m_model_object->config;
|
||||
const DynamicPrintConfig& print_cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
std::unique_ptr<DynamicPrintConfig> default_cfg = nullptr;
|
||||
|
||||
for (const std::string& key : keys) {
|
||||
|
@ -2255,6 +2282,18 @@ std::vector<ConfigOption*> GLGizmoSlaSupports::get_config_options(const std::vec
|
|||
}
|
||||
|
||||
|
||||
void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const
|
||||
{
|
||||
int idx = 0;
|
||||
Eigen::Matrix<float, 1, 3> pp = m_editing_mode_cache[i].support_point.pos;
|
||||
Eigen::Matrix<float, 1, 3> cc;
|
||||
m_AABB.squared_distance(m_V, m_F, pp, idx, cc);
|
||||
Vec3f a = (m_V.row(m_F(idx, 1)) - m_V.row(m_F(idx, 0)));
|
||||
Vec3f b = (m_V.row(m_F(idx, 2)) - m_V.row(m_F(idx, 0)));
|
||||
m_editing_mode_cache[i].normal = a.cross(b);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if ENABLE_IMGUI
|
||||
void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const GLCanvas3D::Selection& selection)
|
||||
|
@ -2268,7 +2307,7 @@ RENDER_AGAIN:
|
|||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
|
||||
const float scaling = m_imgui->get_style_scaling();
|
||||
const ImVec2 window_size(285.f * scaling, 260.f * scaling);
|
||||
const ImVec2 window_size(285.f * scaling, 300.f * scaling);
|
||||
ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) ));
|
||||
ImGui::SetNextWindowSize(ImVec2(window_size));
|
||||
|
||||
|
@ -2287,30 +2326,19 @@ RENDER_AGAIN:
|
|||
m_imgui->text(_(L("Shift + Left (+ drag) - select point(s)")));
|
||||
m_imgui->text(" "); // vertical gap
|
||||
|
||||
static const std::vector<float> options = {0.2f, 0.4f, 0.6f, 0.8f, 1.0f};
|
||||
static const std::vector<std::string> options_str = {"0.2", "0.4", "0.6", "0.8", "1.0"};
|
||||
int selection = -1;
|
||||
for (size_t i = 0; i < options.size(); i++) {
|
||||
if (options[i] == m_new_point_head_diameter) { selection = (int)i; }
|
||||
}
|
||||
float diameter_upper_cap = static_cast<ConfigOptionFloat*>(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value;
|
||||
if (m_new_point_head_diameter > diameter_upper_cap)
|
||||
m_new_point_head_diameter = diameter_upper_cap;
|
||||
|
||||
bool old_combo_state = m_combo_box_open;
|
||||
// The combo is commented out for now, until the feature is supported by backend.
|
||||
// m_combo_box_open = m_imgui->combo(_(L("Head diameter")), options_str, selection);
|
||||
force_refresh |= (old_combo_state != m_combo_box_open);
|
||||
|
||||
// float current_number = atof(str);
|
||||
const float current_number = selection < options.size() ? options[selection] : m_new_point_head_diameter;
|
||||
if (old_combo_state && !m_combo_box_open) // closing the combo must always change the sizes (even if the selection did not change)
|
||||
for (auto& point_and_selection : m_editing_mode_cache)
|
||||
if (point_and_selection.second) {
|
||||
point_and_selection.first.head_front_radius = current_number / 2.f;
|
||||
m_imgui->text(_(L("Head diameter: ")));
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SliderFloat("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f")) {
|
||||
// value was changed
|
||||
for (auto& cache_entry : m_editing_mode_cache)
|
||||
if (cache_entry.selected) {
|
||||
cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f;
|
||||
m_unsaved_changes = true;
|
||||
}
|
||||
|
||||
if (std::abs(current_number - m_new_point_head_diameter) > 0.001) {
|
||||
force_refresh = true;
|
||||
m_new_point_head_diameter = current_number;
|
||||
}
|
||||
|
||||
bool changed = m_lock_unique_islands;
|
||||
|
@ -2343,9 +2371,9 @@ RENDER_AGAIN:
|
|||
m_imgui->text(_(L("Minimal points distance: ")));
|
||||
ImGui::SameLine();
|
||||
|
||||
std::vector<ConfigOption*> opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"});
|
||||
float density = static_cast<ConfigOptionInt*>(opts[0])->value;
|
||||
float minimal_point_distance = static_cast<ConfigOptionFloat*>(opts[1])->value;
|
||||
std::vector<const ConfigOption*> opts = get_config_options({"support_points_density_relative", "support_points_minimal_distance"});
|
||||
float density = static_cast<const ConfigOptionInt*>(opts[0])->value;
|
||||
float minimal_point_distance = static_cast<const ConfigOptionFloat*>(opts[1])->value;
|
||||
|
||||
bool value_changed = ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm");
|
||||
if (value_changed)
|
||||
|
@ -2451,6 +2479,10 @@ void GLGizmoSlaSupports::on_set_state()
|
|||
m_parent.toggle_model_objects_visibility(false);
|
||||
if (m_model_object)
|
||||
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
|
||||
|
||||
// Set default head diameter from config.
|
||||
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
|
||||
}
|
||||
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
|
||||
if (m_model_object) {
|
||||
|
@ -2487,12 +2519,16 @@ void GLGizmoSlaSupports::select_point(int i)
|
|||
{
|
||||
if (i == AllPoints || i == NoPoints) {
|
||||
for (auto& point_and_selection : m_editing_mode_cache)
|
||||
point_and_selection.second = ( i == AllPoints );
|
||||
point_and_selection.selected = ( i == AllPoints );
|
||||
m_selection_empty = (i == NoPoints);
|
||||
|
||||
if (i == AllPoints)
|
||||
m_new_point_head_diameter = m_editing_mode_cache[0].support_point.head_front_radius * 2.f;
|
||||
}
|
||||
else {
|
||||
m_editing_mode_cache[i].second = true;
|
||||
m_editing_mode_cache[i].selected = true;
|
||||
m_selection_empty = false;
|
||||
m_new_point_head_diameter = m_editing_mode_cache[i].support_point.head_front_radius * 2.f;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2502,7 +2538,7 @@ void GLGizmoSlaSupports::editing_mode_discard_changes()
|
|||
{
|
||||
m_editing_mode_cache.clear();
|
||||
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
|
||||
m_editing_mode_cache.push_back(std::make_pair(point, false));
|
||||
m_editing_mode_cache.emplace_back(point, false);
|
||||
m_editing_mode = false;
|
||||
m_unsaved_changes = false;
|
||||
}
|
||||
|
@ -2516,8 +2552,8 @@ void GLGizmoSlaSupports::editing_mode_apply_changes()
|
|||
if (m_unsaved_changes) {
|
||||
m_model_object->sla_points_status = sla::PointsStatus::UserModified;
|
||||
m_model_object->sla_support_points.clear();
|
||||
for (const std::pair<sla::SupportPoint, bool>& point_and_selection : m_editing_mode_cache)
|
||||
m_model_object->sla_support_points.push_back(point_and_selection.first);
|
||||
for (const CacheEntry& cache_entry : m_editing_mode_cache)
|
||||
m_model_object->sla_support_points.push_back(cache_entry.support_point);
|
||||
|
||||
// Recalculate support structures once the editing mode is left.
|
||||
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
|
@ -2534,7 +2570,7 @@ void GLGizmoSlaSupports::editing_mode_reload_cache()
|
|||
{
|
||||
m_editing_mode_cache.clear();
|
||||
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
|
||||
m_editing_mode_cache.push_back(std::make_pair(point, false));
|
||||
m_editing_mode_cache.emplace_back(point, false);
|
||||
|
||||
m_unsaved_changes = false;
|
||||
}
|
||||
|
|
|
@ -471,7 +471,7 @@ private:
|
|||
ModelObject* m_old_model_object = nullptr;
|
||||
int m_active_instance = -1;
|
||||
int m_old_instance_id = -1;
|
||||
Vec3f unproject_on_mesh(const Vec2d& mouse_pos);
|
||||
std::pair<Vec3f, Vec3f> unproject_on_mesh(const Vec2d& mouse_pos);
|
||||
|
||||
const float RenderPointScale = 1.f;
|
||||
|
||||
|
@ -484,6 +484,16 @@ private:
|
|||
Geometry::Transformation transformation;
|
||||
};
|
||||
|
||||
class CacheEntry {
|
||||
public:
|
||||
CacheEntry(const sla::SupportPoint& point, bool sel, const Vec3f& norm = Vec3f::Zero()) :
|
||||
support_point(point), selected(sel), normal(norm) {}
|
||||
|
||||
sla::SupportPoint support_point;
|
||||
bool selected; // whether the point is selected
|
||||
Vec3f normal;
|
||||
};
|
||||
|
||||
// This holds information to decide whether recalculation is necessary:
|
||||
SourceDataSummary m_source_data;
|
||||
|
||||
|
@ -510,6 +520,7 @@ private:
|
|||
void render_points(const GLCanvas3D::Selection& selection, bool picking = false) const;
|
||||
bool is_mesh_update_necessary() const;
|
||||
void update_mesh();
|
||||
void update_cache_entry_normal(unsigned int i) const;
|
||||
|
||||
#if !ENABLE_IMGUI
|
||||
void render_tooltip_texture() const;
|
||||
|
@ -520,10 +531,10 @@ private:
|
|||
bool m_lock_unique_islands = false;
|
||||
bool m_editing_mode = false; // Is editing mode active?
|
||||
bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes).
|
||||
float m_new_point_head_diameter = 0.4f; // Size of a new point.
|
||||
float m_new_point_head_diameter; // Size of a new point.
|
||||
float m_minimal_point_distance = 20.f;
|
||||
float m_density = 100.f;
|
||||
std::vector<std::pair<sla::SupportPoint, bool>> m_editing_mode_cache; // a support point and whether it is currently selected
|
||||
mutable std::vector<CacheEntry> m_editing_mode_cache; // a support point and whether it is currently selected
|
||||
|
||||
bool m_selection_rectangle_active = false;
|
||||
Vec2d m_selection_rectangle_start_corner;
|
||||
|
@ -536,7 +547,7 @@ private:
|
|||
int m_canvas_width;
|
||||
int m_canvas_height;
|
||||
|
||||
std::vector<ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
|
||||
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
|
||||
|
||||
// Methods that do the model_object and editing cache synchronization,
|
||||
// editing mode selection, etc:
|
||||
|
|
|
@ -167,17 +167,6 @@ bool GUI_App::OnInit()
|
|||
if (app_config->dirty() && app_config->get("autosave") == "1")
|
||||
app_config->save();
|
||||
|
||||
// ! Temporary workaround for the correct behavior of the Scrolled sidebar panel
|
||||
// Do this "manipulations" only once ( after (re)create of the application )
|
||||
if (sidebar().obj_list()->GetMinHeight() > 15 * wxGetApp().em_unit())
|
||||
{
|
||||
wxWindowUpdateLocker noUpdates_sidebar(&sidebar());
|
||||
sidebar().obj_list()->SetMinSize(wxSize(-1, 15 * wxGetApp().em_unit()));
|
||||
|
||||
// !!! to correct later layouts
|
||||
update_mode(); // update view mode after fix of the object_list size
|
||||
}
|
||||
|
||||
this->obj_manipul()->update_if_dirty();
|
||||
|
||||
// Preset updating & Configwizard are done after the above initializations,
|
||||
|
@ -205,12 +194,13 @@ bool GUI_App::OnInit()
|
|||
}
|
||||
preset_updater->sync(preset_bundle);
|
||||
});
|
||||
|
||||
load_current_presets();
|
||||
}
|
||||
});
|
||||
|
||||
load_current_presets();
|
||||
|
||||
mainframe->Show(true);
|
||||
update_mode(); // update view mode after fix of the object_list size
|
||||
m_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -115,10 +115,7 @@ ObjectList::~ObjectList()
|
|||
|
||||
void ObjectList::create_objects_ctrl()
|
||||
{
|
||||
// temporary workaround for the correct behavior of the Scrolled sidebar panel:
|
||||
// 1. set a height of the list to some big value
|
||||
// 2. change it to the normal min value (200) after first whole App updating/layouting
|
||||
SetMinSize(wxSize(-1, 3000)); // #ys_FIXME
|
||||
SetMinSize(wxSize(-1, 15 * wxGetApp().em_unit()));
|
||||
|
||||
m_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_sizer->Add(this, 1, wxGROW);
|
||||
|
@ -430,10 +427,8 @@ void ObjectList::OnContextMenu(wxDataViewEvent&)
|
|||
else if (title == _("Name") && pt.x >15 &&
|
||||
m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.GetRefData())
|
||||
{
|
||||
if (is_windows10()) {
|
||||
const auto obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
|
||||
wxGetApp().plater()->fix_through_netfabb(obj_idx);
|
||||
}
|
||||
if (is_windows10())
|
||||
fix_through_netfabb();
|
||||
}
|
||||
#ifndef __WXMSW__
|
||||
GetMainWindow()->SetToolTip(""); // hide tooltip
|
||||
|
@ -1522,12 +1517,7 @@ bool ObjectList::is_splittable()
|
|||
if (!get_volume_by_item(item, volume) || !volume)
|
||||
return false;
|
||||
|
||||
int splittable = volume->is_splittable();
|
||||
if (splittable == -1) {
|
||||
splittable = (int)volume->mesh.has_multiple_patches();
|
||||
volume->set_splittable(splittable);
|
||||
}
|
||||
return splittable != 0;
|
||||
return volume->is_splittable();
|
||||
}
|
||||
|
||||
bool ObjectList::selected_instances_of_same_object()
|
||||
|
@ -2280,13 +2270,37 @@ void ObjectList::fix_through_netfabb() const
|
|||
if (!item)
|
||||
return;
|
||||
|
||||
ItemType type = m_objects_model->GetItemType(item);
|
||||
const ItemType type = m_objects_model->GetItemType(item);
|
||||
|
||||
if (type & itObject)
|
||||
wxGetApp().plater()->fix_through_netfabb(m_objects_model->GetIdByItem(item));
|
||||
else if (type & itVolume)
|
||||
wxGetApp().plater()->fix_through_netfabb(m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)),
|
||||
m_objects_model->GetVolumeIdByItem(item));
|
||||
const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
|
||||
type & itVolume ? m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)) : -1;
|
||||
|
||||
const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1;
|
||||
|
||||
wxGetApp().plater()->fix_through_netfabb(obj_idx, vol_idx);
|
||||
|
||||
update_item_error_icon(obj_idx, vol_idx);
|
||||
}
|
||||
|
||||
void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) const
|
||||
{
|
||||
const wxDataViewItem item = vol_idx <0 ? m_objects_model->GetItemById(obj_idx) :
|
||||
m_objects_model->GetItemByVolumeId(obj_idx, vol_idx);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
auto model_object = (*m_objects)[obj_idx];
|
||||
|
||||
const stl_stats& stats = model_object->volumes[vol_idx<0 ? 0 : vol_idx]->mesh.stl.stats;
|
||||
const int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
|
||||
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
|
||||
|
||||
if (errors == 0) {
|
||||
// delete Error_icon if all errors are fixed
|
||||
wxVariant variant;
|
||||
variant << PrusaDataViewBitmapText(from_u8(model_object->name), wxNullBitmap);
|
||||
m_objects_model->SetValue(variant, item, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectList::ItemValueChanged(wxDataViewEvent &event)
|
||||
|
|
|
@ -270,6 +270,7 @@ public:
|
|||
void split_instances();
|
||||
void rename_item();
|
||||
void fix_through_netfabb() const;
|
||||
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
|
||||
private:
|
||||
void OnChar(wxKeyEvent& event);
|
||||
void OnContextMenu(wxDataViewEvent &event);
|
||||
|
|
|
@ -33,9 +33,6 @@ namespace GUI {
|
|||
#if !ENABLE_IMGUI
|
||||
, m_gizmo_widget(nullptr)
|
||||
#endif // !ENABLE_IMGUI
|
||||
, m_model(nullptr)
|
||||
, m_config(nullptr)
|
||||
, m_process(nullptr)
|
||||
{
|
||||
init(parent, bed, camera, view_toolbar, model, config, process);
|
||||
}
|
||||
|
@ -202,18 +199,15 @@ Preview::Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_t
|
|||
, m_enabled(false)
|
||||
, m_schedule_background_process(schedule_background_process_func)
|
||||
{
|
||||
if (init(parent, bed, camera, view_toolbar, config, process, gcode_preview_data))
|
||||
if (init(parent, bed, camera, view_toolbar))
|
||||
{
|
||||
show_hide_ui_elements("none");
|
||||
load_print();
|
||||
}
|
||||
}
|
||||
|
||||
bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data)
|
||||
bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
|
||||
{
|
||||
if ((config == nullptr) || (process == nullptr) || (gcode_preview_data == nullptr))
|
||||
return false;
|
||||
|
||||
if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 /* disable wxTAB_TRAVERSAL */))
|
||||
return false;
|
||||
|
||||
|
@ -222,7 +216,7 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view
|
|||
m_canvas = _3DScene::get_canvas(this->m_canvas_widget);
|
||||
m_canvas->allow_multisample(GLCanvas3DManager::can_multisample());
|
||||
m_canvas->set_config(m_config);
|
||||
m_canvas->set_process(process);
|
||||
m_canvas->set_process(m_process);
|
||||
m_canvas->enable_legend_texture(true);
|
||||
m_canvas->enable_dynamic_background(true);
|
||||
|
||||
|
|
|
@ -39,10 +39,6 @@ class View3D : public wxPanel
|
|||
wxPanel* m_gizmo_widget;
|
||||
#endif // !ENABLE_IMGUI
|
||||
|
||||
Model* m_model;
|
||||
DynamicPrintConfig* m_config;
|
||||
BackgroundSlicingProcess* m_process;
|
||||
|
||||
public:
|
||||
View3D(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process);
|
||||
virtual ~View3D();
|
||||
|
@ -124,7 +120,7 @@ public:
|
|||
void refresh_print();
|
||||
|
||||
private:
|
||||
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data);
|
||||
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar);
|
||||
|
||||
void bind_event_handlers();
|
||||
void unbind_event_handlers();
|
||||
|
|
|
@ -107,11 +107,11 @@ bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt)
|
|||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.MousePos = ImVec2((float)evt.GetX(), (float)evt.GetY());
|
||||
io.MouseDown[0] = evt.LeftDown();
|
||||
io.MouseDown[1] = evt.RightDown();
|
||||
io.MouseDown[2] = evt.MiddleDown();
|
||||
io.MouseDown[0] = evt.LeftIsDown();
|
||||
io.MouseDown[1] = evt.RightIsDown();
|
||||
io.MouseDown[2] = evt.MiddleIsDown();
|
||||
|
||||
unsigned buttons = (evt.LeftDown() ? 1 : 0) | (evt.RightDown() ? 2 : 0) | (evt.MiddleDown() ? 4 : 0);
|
||||
unsigned buttons = (evt.LeftIsDown() ? 1 : 0) | (evt.RightIsDown() ? 2 : 0) | (evt.MiddleIsDown() ? 4 : 0);
|
||||
m_mouse_buttons = buttons;
|
||||
|
||||
new_frame();
|
||||
|
@ -441,6 +441,10 @@ void ImGuiWrapper::init_style()
|
|||
set_color(ImGuiCol_Header, COL_ORANGE_DARK);
|
||||
set_color(ImGuiCol_HeaderHovered, COL_ORANGE_LIGHT);
|
||||
set_color(ImGuiCol_HeaderActive, COL_ORANGE_LIGHT);
|
||||
|
||||
// Slider
|
||||
set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK);
|
||||
set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT);
|
||||
}
|
||||
|
||||
void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
|
||||
|
|
|
@ -524,6 +524,7 @@ struct Sidebar::priv
|
|||
Plater *plater;
|
||||
|
||||
wxScrolledWindow *scrolled;
|
||||
wxPanel* presets_panel; // Used for MSW better layouts
|
||||
|
||||
PrusaModeSizer *mode_sizer;
|
||||
wxFlexGridSizer *sizer_presets;
|
||||
|
@ -555,8 +556,6 @@ void Sidebar::priv::show_preset_comboboxes()
|
|||
{
|
||||
const bool showSLA = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA;
|
||||
|
||||
wxWindowUpdateLocker noUpdates_scrolled(scrolled->GetParent());
|
||||
|
||||
for (size_t i = 0; i < 4; ++i)
|
||||
sizer_presets->Show(i, !showSLA);
|
||||
|
||||
|
@ -580,6 +579,7 @@ Sidebar::Sidebar(Plater *parent)
|
|||
p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(40 * wxGetApp().em_unit(), -1));
|
||||
p->scrolled->SetScrollbars(0, 20, 1, 2);
|
||||
|
||||
|
||||
// Sizer in the scrolled area
|
||||
auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
p->scrolled->SetSizer(scrolled_sizer);
|
||||
|
@ -591,12 +591,25 @@ Sidebar::Sidebar(Plater *parent)
|
|||
p->sizer_presets = new wxFlexGridSizer(10, 1, 1, 2);
|
||||
p->sizer_presets->AddGrowableCol(0, 1);
|
||||
p->sizer_presets->SetFlexibleDirection(wxBOTH);
|
||||
|
||||
bool is_msw = false;
|
||||
#ifdef __WINDOWS__
|
||||
p->scrolled->SetDoubleBuffered(true);
|
||||
|
||||
p->presets_panel = new wxPanel(p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
p->presets_panel->SetSizer(p->sizer_presets);
|
||||
|
||||
is_msw = true;
|
||||
#else
|
||||
p->presets_panel = p->scrolled;
|
||||
#endif //__WINDOWS__
|
||||
|
||||
p->sizer_filaments = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto init_combo = [this](PresetComboBox **combo, wxString label, Preset::Type preset_type, bool filament) {
|
||||
auto *text = new wxStaticText(p->scrolled, wxID_ANY, label+" :");
|
||||
auto *text = new wxStaticText(p->presets_panel, wxID_ANY, label + " :");
|
||||
text->SetFont(wxGetApp().small_font());
|
||||
*combo = new PresetComboBox(p->scrolled, preset_type);
|
||||
*combo = new PresetComboBox(p->presets_panel, preset_type);
|
||||
|
||||
auto *sizer_presets = this->p->sizer_presets;
|
||||
auto *sizer_filaments = this->p->sizer_filaments;
|
||||
|
@ -645,9 +658,7 @@ Sidebar::Sidebar(Plater *parent)
|
|||
p->object_settings->Hide();
|
||||
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
|
||||
|
||||
wxBitmap arrow_up(GUI::from_u8(Slic3r::var("brick_go.png")), wxBITMAP_TYPE_PNG);
|
||||
p->btn_send_gcode = new wxButton(this, wxID_ANY, _(L("Send to printer")));
|
||||
p->btn_send_gcode->SetBitmap(arrow_up);
|
||||
p->btn_send_gcode->SetFont(wxGetApp().bold_font());
|
||||
p->btn_send_gcode->Hide();
|
||||
|
||||
|
@ -657,6 +668,8 @@ Sidebar::Sidebar(Plater *parent)
|
|||
|
||||
// Sizer in the scrolled area
|
||||
scrolled_sizer->Add(p->mode_sizer, 0, wxALIGN_CENTER_HORIZONTAL/*RIGHT | wxBOTTOM | wxRIGHT, 5*/);
|
||||
is_msw ?
|
||||
scrolled_sizer->Add(p->presets_panel, 0, wxEXPAND | wxLEFT, margin_5) :
|
||||
scrolled_sizer->Add(p->sizer_presets, 0, wxEXPAND | wxLEFT, margin_5);
|
||||
scrolled_sizer->Add(p->sizer_params, 1, wxEXPAND | wxLEFT, margin_5);
|
||||
scrolled_sizer->Add(p->object_info, 0, wxEXPAND | wxTOP | wxLEFT, margin_5);
|
||||
|
@ -695,7 +708,7 @@ Sidebar::Sidebar(Plater *parent)
|
|||
Sidebar::~Sidebar() {}
|
||||
|
||||
void Sidebar::init_filament_combo(PresetComboBox **combo, const int extr_idx) {
|
||||
*combo = new PresetComboBox(p->scrolled, Slic3r::Preset::TYPE_FILAMENT);
|
||||
*combo = new PresetComboBox(p->presets_panel, Slic3r::Preset::TYPE_FILAMENT);
|
||||
// # copy icons from first choice
|
||||
// $choice->SetItemBitmap($_, $choices->[0]->GetItemBitmap($_)) for 0..$#presets;
|
||||
|
||||
|
@ -757,6 +770,8 @@ void Sidebar::update_presets(Preset::Type preset_type)
|
|||
|
||||
case Preset::TYPE_PRINTER:
|
||||
{
|
||||
// wxWindowUpdateLocker noUpdates_scrolled(p->scrolled);
|
||||
|
||||
// Update the print choosers to only contain the compatible presets, update the dirty flags.
|
||||
if (print_tech == ptFFF)
|
||||
preset_bundle.prints.update_platter_ui(p->combo_print);
|
||||
|
@ -817,6 +832,11 @@ wxScrolledWindow* Sidebar::scrolled_panel()
|
|||
return p->scrolled;
|
||||
}
|
||||
|
||||
wxPanel* Sidebar::presets_panel()
|
||||
{
|
||||
return p->presets_panel;
|
||||
}
|
||||
|
||||
ConfigOptionsGroup* Sidebar::og_freq_chng_params(const bool is_fff)
|
||||
{
|
||||
return p->frequently_changed_parameters->get_og(is_fff);
|
||||
|
@ -2390,7 +2410,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
|||
//! instead of
|
||||
//! combo->GetStringSelection().ToUTF8().data());
|
||||
|
||||
std::string selected_string = combo->GetString(combo->GetSelection()).ToUTF8().data();
|
||||
const std::string& selected_string = combo->GetString(combo->GetSelection()).ToUTF8().data();
|
||||
|
||||
if (preset_type == Preset::TYPE_FILAMENT) {
|
||||
wxGetApp().preset_bundle->set_filament_preset(idx, selected_string);
|
||||
|
@ -2402,12 +2422,8 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
|||
wxGetApp().preset_bundle->update_platter_filament_ui(idx, combo);
|
||||
}
|
||||
else {
|
||||
for (Tab* tab : wxGetApp().tabs_list) {
|
||||
if (tab->type() == preset_type) {
|
||||
tab->select_preset(selected_string);
|
||||
break;
|
||||
}
|
||||
}
|
||||
wxWindowUpdateLocker noUpdates(sidebar->presets_panel());
|
||||
wxGetApp().get_tab(preset_type)->select_preset(selected_string);
|
||||
}
|
||||
|
||||
// update plater with new config
|
||||
|
@ -2463,14 +2479,17 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
|
|||
this->statusbar()->reset_cancel_callback();
|
||||
this->statusbar()->stop_busy();
|
||||
|
||||
bool canceled = evt.GetInt() < 0;
|
||||
bool success = evt.GetInt() > 0;
|
||||
const bool canceled = evt.GetInt() < 0;
|
||||
const bool error = evt.GetInt() == 0;
|
||||
const bool success = evt.GetInt() > 0;
|
||||
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
|
||||
this->background_process.reset_export();
|
||||
if (! success) {
|
||||
|
||||
if (error) {
|
||||
wxString message = evt.GetString();
|
||||
if (message.IsEmpty())
|
||||
message = _(L("Export failed"));
|
||||
show_error(q, message);
|
||||
this->statusbar()->set_status_text(message);
|
||||
}
|
||||
if (canceled)
|
||||
|
@ -2890,6 +2909,7 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
|
|||
sidebar->show_export(!is_ready_to_slice);
|
||||
sidebar->show_send(send_gcode_shown && !is_ready_to_slice);
|
||||
}
|
||||
sidebar->Layout();
|
||||
}
|
||||
|
||||
void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const
|
||||
|
@ -3307,8 +3327,10 @@ void Plater::on_extruders_change(int num_extruders)
|
|||
{
|
||||
auto& choices = sidebar().combos_filament();
|
||||
|
||||
if (num_extruders == choices.size())
|
||||
return;
|
||||
|
||||
wxWindowUpdateLocker noUpdates_scrolled_panel(&sidebar()/*.scrolled_panel()*/);
|
||||
// sidebar().scrolled_panel()->Freeze();
|
||||
|
||||
int i = choices.size();
|
||||
while ( i < num_extruders )
|
||||
|
|
|
@ -51,6 +51,8 @@ public:
|
|||
int get_extruder_idx() const { return extruder_idx; }
|
||||
void check_selection();
|
||||
|
||||
std::string selected_preset_name;
|
||||
|
||||
private:
|
||||
typedef std::size_t Marker;
|
||||
enum { LABEL_ITEM_MARKER = 0x4d };
|
||||
|
@ -81,6 +83,7 @@ public:
|
|||
ObjectList* obj_list();
|
||||
ObjectSettings* obj_settings();
|
||||
wxScrolledWindow* scrolled_panel();
|
||||
wxPanel* presets_panel();
|
||||
|
||||
ConfigOptionsGroup* og_freq_chng_params(const bool is_fff);
|
||||
wxButton* get_wiping_dialog_button();
|
||||
|
|
|
@ -457,6 +457,7 @@ const std::vector<std::string>& Preset::sla_print_options()
|
|||
"support_base_height",
|
||||
"support_critical_angle",
|
||||
"support_max_bridge_length",
|
||||
"support_max_pillar_link_distance",
|
||||
"support_object_elevation",
|
||||
"support_points_density_relative",
|
||||
"support_points_minimal_distance",
|
||||
|
@ -878,8 +879,10 @@ size_t PresetCollection::update_compatible_internal(const Preset &active_printer
|
|||
// Hide the
|
||||
void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
|
||||
{
|
||||
if (ui == nullptr)
|
||||
if (ui == nullptr ||
|
||||
ui->selected_preset_name == this->get_selected_preset().name)
|
||||
return;
|
||||
|
||||
// Otherwise fill in the list from scratch.
|
||||
ui->Freeze();
|
||||
ui->Clear();
|
||||
|
@ -948,6 +951,8 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
|
|||
ui->SetSelection(selected_preset_item);
|
||||
ui->SetToolTip(ui->GetString(selected_preset_item));
|
||||
ui->Thaw();
|
||||
|
||||
ui->selected_preset_name = this->get_selected_preset().name;
|
||||
}
|
||||
|
||||
size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible)
|
||||
|
|
|
@ -1436,7 +1436,8 @@ bool PresetBundle::parse_color(const std::string &scolor, unsigned char *rgb_out
|
|||
void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui)
|
||||
{
|
||||
if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA ||
|
||||
this->filament_presets.size() <= idx_extruder )
|
||||
this->filament_presets.size() <= idx_extruder ||
|
||||
ui->selected_preset_name == this->filaments.find_preset(this->filament_presets[idx_extruder])->name)
|
||||
return;
|
||||
|
||||
unsigned char rgb[3];
|
||||
|
@ -1525,6 +1526,8 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr
|
|||
ui->SetSelection(selected_preset_item);
|
||||
ui->SetToolTip(ui->GetString(selected_preset_item));
|
||||
ui->Thaw();
|
||||
|
||||
ui->selected_preset_name = this->filaments.find_preset(this->filament_presets[idx_extruder])->name;
|
||||
}
|
||||
|
||||
void PresetBundle::set_default_suppressed(bool default_suppressed)
|
||||
|
|
|
@ -73,6 +73,10 @@ void Tab::set_type()
|
|||
// sub new
|
||||
void Tab::create_preset_tab()
|
||||
{
|
||||
#ifdef __WINDOWS__
|
||||
SetDoubleBuffered(true);
|
||||
#endif //__WINDOWS__
|
||||
|
||||
m_preset_bundle = wxGetApp().preset_bundle;
|
||||
|
||||
// Vertical sizer to hold the choice menu and the rest of the page.
|
||||
|
@ -751,6 +755,10 @@ void Tab::load_key_value(const std::string& opt_key, const boost::any& value, bo
|
|||
|
||||
void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
{
|
||||
if (wxGetApp().plater() == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_fff = supports_printer_technology(ptFFF);
|
||||
ConfigOptionsGroup* og_freq_chng_params = wxGetApp().sidebar().og_freq_chng_params(is_fff);
|
||||
if (opt_key == "fill_density" || opt_key == "pad_enable")
|
||||
|
@ -783,25 +791,6 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
|||
wxGetApp().plater()->on_extruders_change(boost::any_cast<size_t>(value));
|
||||
|
||||
update();
|
||||
|
||||
// #ys_FIXME_to_delete
|
||||
// Post event to the Plater after updating of the all dirty options
|
||||
// It helps to avoid needless schedule_background_processing
|
||||
// if (update_completed())
|
||||
// if (m_update_stack.empty())
|
||||
// {
|
||||
// // wxCommandEvent event(EVT_TAB_VALUE_CHANGED);
|
||||
// // event.SetEventObject(this);
|
||||
// // event.SetString(opt_key);
|
||||
// // if (opt_key == "extruders_count")
|
||||
// // {
|
||||
// // const int val = boost::any_cast<size_t>(value);
|
||||
// // event.SetInt(val);
|
||||
// // }
|
||||
// //
|
||||
// // wxPostEvent(this, event);
|
||||
// wxGetApp().mainframe->on_value_changed(m_config);
|
||||
// }
|
||||
}
|
||||
|
||||
// Show/hide the 'purging volumes' button
|
||||
|
@ -824,9 +813,13 @@ void Tab::update_wiping_button_visibility() {
|
|||
// To update the content of the selection boxes,
|
||||
// to update the filament colors of the selection boxes,
|
||||
// to update the "dirty" flags of the selection boxes,
|
||||
// to uddate number of "filament" selection boxes when the number of extruders change.
|
||||
// to update number of "filament" selection boxes when the number of extruders change.
|
||||
void Tab::on_presets_changed()
|
||||
{
|
||||
// Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets
|
||||
wxGetApp().plater()->sidebar().update_presets(m_type);
|
||||
update_preset_description_line();
|
||||
|
||||
// Printer selected at the Printer tab, update "compatible" marks at the print and filament selectors.
|
||||
for (auto t: m_dependent_tabs)
|
||||
{
|
||||
|
@ -837,16 +830,6 @@ void Tab::on_presets_changed()
|
|||
// clear m_dependent_tabs after first update from select_preset()
|
||||
// to avoid needless preset loading from update() function
|
||||
m_dependent_tabs.clear();
|
||||
|
||||
// #ys_FIXME_to_delete
|
||||
// wxCommandEvent event(EVT_TAB_PRESETS_CHANGED);
|
||||
// event.SetEventObject(this);
|
||||
// wxPostEvent(this, event);
|
||||
|
||||
// Instead of PostEvent (EVT_TAB_PRESETS_CHANGED) just call update_presets
|
||||
wxGetApp().plater()->sidebar().update_presets(m_type);
|
||||
|
||||
update_preset_description_line();
|
||||
}
|
||||
|
||||
void Tab::update_preset_description_line()
|
||||
|
@ -2472,7 +2455,7 @@ void Tab::load_current_preset()
|
|||
//Regerenerate content of the page tree.
|
||||
void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/)
|
||||
{
|
||||
Freeze();
|
||||
// Freeze();
|
||||
|
||||
// get label of the currently selected item
|
||||
const auto sel_item = m_treectrl->GetSelection();
|
||||
|
@ -2498,7 +2481,7 @@ void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/)
|
|||
// this is triggered on first load, so we don't disable the sel change event
|
||||
m_treectrl->SelectItem(m_treectrl->GetFirstVisibleItem());//! (treectrl->GetFirstChild(rootItem));
|
||||
}
|
||||
Thaw();
|
||||
// Thaw();
|
||||
}
|
||||
|
||||
void Tab::update_page_tree_visibility()
|
||||
|
@ -3273,7 +3256,8 @@ void TabSLAPrint::build()
|
|||
optgroup->append_single_option_line("support_pillar_diameter");
|
||||
optgroup->append_single_option_line("support_pillar_connection_mode");
|
||||
optgroup->append_single_option_line("support_buildplate_only");
|
||||
optgroup->append_single_option_line("support_pillar_widening_factor");
|
||||
// TODO: This parameter is not used at the moment.
|
||||
// optgroup->append_single_option_line("support_pillar_widening_factor");
|
||||
optgroup->append_single_option_line("support_base_diameter");
|
||||
optgroup->append_single_option_line("support_base_height");
|
||||
optgroup->append_single_option_line("support_object_elevation");
|
||||
|
@ -3281,6 +3265,7 @@ void TabSLAPrint::build()
|
|||
optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions")));
|
||||
optgroup->append_single_option_line("support_critical_angle");
|
||||
optgroup->append_single_option_line("support_max_bridge_length");
|
||||
optgroup->append_single_option_line("support_max_pillar_link_distance");
|
||||
|
||||
optgroup = page->new_optgroup(_(L("Automatic generation")));
|
||||
optgroup->append_single_option_line("support_points_density_relative");
|
||||
|
@ -3339,11 +3324,37 @@ void TabSLAPrint::update()
|
|||
return; // #ys_FIXME
|
||||
|
||||
// #ys_FIXME
|
||||
// m_update_cnt++;
|
||||
// ! something to update
|
||||
// m_update_cnt--;
|
||||
//
|
||||
// if (m_update_cnt == 0)
|
||||
m_update_cnt++;
|
||||
|
||||
double head_penetration = m_config->opt_float("support_head_penetration");
|
||||
double head_width = m_config->opt_float("support_head_width");
|
||||
if(head_penetration > head_width) {
|
||||
wxString msg_text = _(L("Head penetration should not be greater than the head width."));
|
||||
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Invalid Head penetration")), wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
if (dialog->ShowModal() == wxID_OK) {
|
||||
new_conf.set_key_value("support_head_penetration", new ConfigOptionFloat(head_width));
|
||||
}
|
||||
|
||||
load_config(new_conf);
|
||||
}
|
||||
|
||||
double pinhead_d = m_config->opt_float("support_head_front_diameter");
|
||||
double pillar_d = m_config->opt_float("support_pillar_diameter");
|
||||
if(pinhead_d > pillar_d) {
|
||||
wxString msg_text = _(L("Pinhead diameter should be smaller than the pillar diameter."));
|
||||
auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Invalid pinhead diameter")), wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
if (dialog->ShowModal() == wxID_OK) {
|
||||
new_conf.set_key_value("support_head_front_diameter", new ConfigOptionFloat(pillar_d / 2.0));
|
||||
}
|
||||
|
||||
load_config(new_conf);
|
||||
}
|
||||
|
||||
m_update_cnt--;
|
||||
|
||||
if (m_update_cnt == 0)
|
||||
wxGetApp().mainframe->on_config_changed(m_config);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue