Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_custom_bed

This commit is contained in:
Enrico Turri 2019-07-15 08:19:58 +02:00
commit 610e0f7ee6
56 changed files with 1779 additions and 1171 deletions

View file

@ -46,7 +46,7 @@ BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 75
ColumnLimit: 78
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true

2
deps/CMakeLists.txt vendored
View file

@ -105,7 +105,7 @@ else()
dep_gtest
dep_nlopt
dep_qhull
dep_libigl
# dep_libigl # Not working, static build has different Eigen
)
endif()

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

View file

@ -15,7 +15,8 @@ const std::string USAGE_STR = {
namespace Slic3r { namespace sla {
Contour3D create_base_pool(const ExPolygons &ground_layer,
Contour3D create_base_pool(const Polygons &ground_layer,
const Polygons &holes = {},
const PoolConfig& cfg = PoolConfig());
Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling,
@ -42,37 +43,28 @@ int main(const int argc, const char *argv[]) {
model.ReadSTLFile(argv[1]);
model.align_to_origin();
ExPolygons ground_slice;
sla::Contour3D mesh;
// TriangleMesh basepool;
Polygons ground_slice;
sla::base_plate(model, ground_slice, 0.1f);
if(ground_slice.empty()) return EXIT_FAILURE;
// ExPolygon bottom_plate = ground_slice.front();
// ExPolygon top_plate = bottom_plate;
// sla::offset(top_plate, coord_t(3.0/SCALING_FACTOR));
// sla::offset(bottom_plate, coord_t(1.0/SCALING_FACTOR));
Polygon gndfirst; gndfirst = ground_slice.front();
sla::offset_with_breakstick_holes(gndfirst, 0.5, 10, 0.3);
sla::Contour3D mesh;
bench.start();
// TriangleMesh pool;
sla::PoolConfig cfg;
cfg.min_wall_height_mm = 0;
cfg.edge_radius_mm = 0.2;
mesh = sla::create_base_pool(ground_slice, cfg);
// mesh.merge(triangulate_expolygon_3d(top_plate, 3.0, false));
// mesh.merge(triangulate_expolygon_3d(bottom_plate, 0.0, true));
// mesh = sla::walls(bottom_plate.contour, top_plate.contour, 0, 3, 2.0, [](){});
cfg.edge_radius_mm = 0;
mesh = sla::create_base_pool(ground_slice, {}, cfg);
bench.stop();
cout << "Base pool creation time: " << std::setprecision(10)
<< bench.getElapsedSec() << " seconds." << endl;
// auto point = []()
for(auto& trind : mesh.indices) {
Vec3d p0 = mesh.points[size_t(trind[0])];
Vec3d p1 = mesh.points[size_t(trind[1])];

View file

@ -586,6 +586,9 @@ void GCode::_do_export(Print &print, FILE *file)
}
m_analyzer.set_extruder_offsets(extruder_offsets);
// tell analyzer about the gcode flavor
m_analyzer.set_gcode_flavor(print.config().gcode_flavor);
// resets analyzer's tracking data
m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm;
m_last_width = GCodeAnalyzer::Default_Width;
@ -1057,6 +1060,10 @@ void GCode::_do_export(Print &print, FILE *file)
print.m_print_statistics.clear();
print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
print.m_print_statistics.estimated_normal_color_print_times = m_normal_time_estimator.get_color_times_dhms();
if (m_silent_time_estimator_enabled)
print.m_print_statistics.estimated_silent_color_print_times = m_silent_time_estimator.get_color_times_dhms();
std::vector<Extruder> extruders = m_writer.extruders();
if (! extruders.empty()) {
std::pair<std::string, unsigned int> out_filament_used_mm ("; filament used [mm] = ", 0);

View file

@ -106,6 +106,11 @@ void GCodeAnalyzer::set_extruder_offsets(const GCodeAnalyzer::ExtruderOffsetsMap
m_extruder_offsets = extruder_offsets;
}
void GCodeAnalyzer::set_gcode_flavor(const GCodeFlavor& flavor)
{
m_gcode_flavor = flavor;
}
void GCodeAnalyzer::reset()
{
_set_units(Millimeters);
@ -249,6 +254,14 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
_processM83(line);
break;
}
case 108:
case 135:
{
// these are used by MakerWare and Sailfish firmwares
// for tool changing - we can process it in one place
_processM108orM135(line);
break;
}
}
break;
@ -426,9 +439,27 @@ void GCodeAnalyzer::_processM600(const GCodeReader::GCodeLine& line)
_set_cp_color_id(m_state.cur_cp_color_id);
}
void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line)
void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line)
{
// These M-codes are used by MakerWare and Sailfish to change active tool.
// They have to be processed otherwise toolchanges will be unrecognised
// by the analyzer - see https://github.com/prusa3d/PrusaSlicer/issues/2566
size_t code = ::atoi(&(line.cmd()[1]));
if ((code == 108 && m_gcode_flavor == gcfSailfish)
|| (code == 135 && m_gcode_flavor == gcfMakerWare)) {
std::string cmd = line.raw();
size_t T_pos = cmd.find("T");
if (T_pos != std::string::npos) {
cmd = cmd.substr(T_pos);
_processT(cmd);
}
}
}
void GCodeAnalyzer::_processT(const std::string& cmd)
{
std::string cmd = line.cmd();
if (cmd.length() > 1)
{
unsigned int id = (unsigned int)::strtol(cmd.substr(1).c_str(), nullptr, 10);
@ -442,6 +473,11 @@ void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line)
}
}
void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line)
{
_processT(line.cmd());
}
bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
{
std::string comment = line.comment();

View file

@ -106,6 +106,7 @@ private:
GCodeReader m_parser;
TypeToMovesMap m_moves_map;
ExtruderOffsetsMap m_extruder_offsets;
GCodeFlavor m_gcode_flavor;
// The output of process_layer()
std::string m_process_output;
@ -115,6 +116,8 @@ public:
void set_extruder_offsets(const ExtruderOffsetsMap& extruder_offsets);
void set_gcode_flavor(const GCodeFlavor& flavor);
// Reinitialize the analyzer
void reset();
@ -164,10 +167,14 @@ private:
// Set extruder to relative mode
void _processM83(const GCodeReader::GCodeLine& line);
// Set tool (MakerWare and Sailfish flavor)
void _processM108orM135(const GCodeReader::GCodeLine& line);
// Set color change
void _processM600(const GCodeReader::GCodeLine& line);
// Processes T line (Select Tool)
void _processT(const std::string& command);
void _processT(const GCodeReader::GCodeLine& line);
// Processes the tags

View file

@ -466,7 +466,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
}
case Extrusion::Tool:
{
unsigned int tools_colors_count = tool_colors.size() / 4;
unsigned int tools_colors_count = (unsigned int)tool_colors.size() / 4;
items.reserve(tools_colors_count);
for (unsigned int i = 0; i < tools_colors_count; ++i)
{
@ -491,17 +491,20 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
items.emplace_back(Slic3r::I18N::translate(L("Default print color")), color);
break;
}
std::string id_str = std::to_string(i + 1) + ": ";
if (i == 0) {
items.emplace_back((boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color);
items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color);
break;
}
if (i == color_print_cnt) {
items.emplace_back((boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i-1].second).str(), color);
items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i - 1].second).str(), color);
continue;
}
// items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1] % cp_values[i]).str(), color);
items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1].second % cp_values[i].first).str(), color);
items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i - 1].second% cp_values[i].first).str(), color);
}
break;
}

View file

@ -174,7 +174,7 @@ namespace Slic3r {
const std::string GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag = "; SILENT_LAST_M73_OUTPUT_PLACEHOLDER";
GCodeTimeEstimator::GCodeTimeEstimator(EMode mode)
: _mode(mode)
: m_mode(mode)
{
reset();
set_default();
@ -183,7 +183,7 @@ namespace Slic3r {
void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line)
{
PROFILE_FUNC();
_parser.parse_line(gcode_line,
m_parser.parse_line(gcode_line,
[this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
{ this->_process_gcode_line(reader, line); });
}
@ -196,7 +196,7 @@ namespace Slic3r {
{ this->_process_gcode_line(reader, line); };
for (; *ptr != 0;) {
gline.reset();
ptr = _parser.parse_line(ptr, gline, action);
ptr = m_parser.parse_line(ptr, gline, action);
}
}
@ -206,10 +206,13 @@ namespace Slic3r {
if (start_from_beginning)
{
_reset_time();
_last_st_synchronized_block_id = -1;
m_last_st_synchronized_block_id = -1;
}
_calculate_time();
if (m_needs_color_times && (m_color_time_cache != 0.0f))
m_color_times.push_back(m_color_time_cache);
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@ -219,12 +222,15 @@ namespace Slic3r {
{
reset();
_parser.parse_buffer(gcode,
m_parser.parse_buffer(gcode,
[this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
{ this->_process_gcode_line(reader, line); });
_calculate_time();
if (m_needs_color_times && (m_color_time_cache != 0.0f))
m_color_times.push_back(m_color_time_cache);
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@ -234,9 +240,12 @@ namespace Slic3r {
{
reset();
_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2));
m_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2));
_calculate_time();
if (m_needs_color_times && (m_color_time_cache != 0.0f))
m_color_times.push_back(m_color_time_cache);
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@ -249,9 +258,12 @@ namespace Slic3r {
auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
{ this->_process_gcode_line(reader, line); };
for (const std::string& line : gcode_lines)
_parser.parse_line(line, action);
m_parser.parse_line(line, action);
_calculate_time();
if (m_needs_color_times && (m_color_time_cache != 0.0f))
m_color_times.push_back(m_color_time_cache);
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@ -270,7 +282,7 @@ namespace Slic3r {
throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n"));
std::string time_mask;
switch (_mode)
switch (m_mode)
{
default:
case Normal:
@ -291,7 +303,7 @@ namespace Slic3r {
// buffer line to export only when greater than 64K to reduce writing calls
std::string export_line;
char time_line[64];
G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin();
G1LineIdToBlockIdMap::const_iterator it_line_id = m_g1_line_ids.begin();
while (std::getline(in, gcode_line))
{
if (!in.good())
@ -301,15 +313,15 @@ namespace Slic3r {
}
// replaces placeholders for initial line M73 with the real lines
if (((_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) ||
((_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)))
if (((m_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) ||
((m_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)))
{
sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(_time).c_str());
sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(m_time).c_str());
gcode_line = time_line;
}
// replaces placeholders for final line M73 with the real lines
else if (((_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) ||
((_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag)))
else if (((m_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) ||
((m_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag)))
{
sprintf(time_line, time_mask.c_str(), "100", "0");
gcode_line = time_line;
@ -319,27 +331,27 @@ namespace Slic3r {
// add remaining time lines where needed
_parser.parse_line(gcode_line,
m_parser.parse_line(gcode_line,
[this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line)
{
if (line.cmd_is("G1"))
{
++g1_lines_count;
assert(it_line_id == _g1_line_ids.end() || it_line_id->first >= g1_lines_count);
assert(it_line_id == m_g1_line_ids.end() || it_line_id->first >= g1_lines_count);
const Block *block = nullptr;
if (it_line_id != _g1_line_ids.end() && it_line_id->first == g1_lines_count) {
if (line.has_e() && it_line_id->second < (unsigned int)_blocks.size())
block = &_blocks[it_line_id->second];
if (it_line_id != m_g1_line_ids.end() && it_line_id->first == g1_lines_count) {
if (line.has_e() && it_line_id->second < (unsigned int)m_blocks.size())
block = &m_blocks[it_line_id->second];
++it_line_id;
}
if (block != nullptr && block->elapsed_time != -1.0f) {
float block_remaining_time = _time - block->elapsed_time;
float block_remaining_time = m_time - block->elapsed_time;
if (std::abs(last_recorded_time - block_remaining_time) > interval)
{
sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / m_time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
gcode_line += time_line;
last_recorded_time = block_remaining_time;
@ -387,240 +399,240 @@ namespace Slic3r {
void GCodeTimeEstimator::set_axis_position(EAxis axis, float position)
{
_state.axis[axis].position = position;
m_state.axis[axis].position = position;
}
void GCodeTimeEstimator::set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec)
{
_state.axis[axis].max_feedrate = feedrate_mm_sec;
m_state.axis[axis].max_feedrate = feedrate_mm_sec;
}
void GCodeTimeEstimator::set_axis_max_acceleration(EAxis axis, float acceleration)
{
_state.axis[axis].max_acceleration = acceleration;
m_state.axis[axis].max_acceleration = acceleration;
}
void GCodeTimeEstimator::set_axis_max_jerk(EAxis axis, float jerk)
{
_state.axis[axis].max_jerk = jerk;
m_state.axis[axis].max_jerk = jerk;
}
float GCodeTimeEstimator::get_axis_position(EAxis axis) const
{
return _state.axis[axis].position;
return m_state.axis[axis].position;
}
float GCodeTimeEstimator::get_axis_max_feedrate(EAxis axis) const
{
return _state.axis[axis].max_feedrate;
return m_state.axis[axis].max_feedrate;
}
float GCodeTimeEstimator::get_axis_max_acceleration(EAxis axis) const
{
return _state.axis[axis].max_acceleration;
return m_state.axis[axis].max_acceleration;
}
float GCodeTimeEstimator::get_axis_max_jerk(EAxis axis) const
{
return _state.axis[axis].max_jerk;
return m_state.axis[axis].max_jerk;
}
void GCodeTimeEstimator::set_feedrate(float feedrate_mm_sec)
{
_state.feedrate = feedrate_mm_sec;
m_state.feedrate = feedrate_mm_sec;
}
float GCodeTimeEstimator::get_feedrate() const
{
return _state.feedrate;
return m_state.feedrate;
}
void GCodeTimeEstimator::set_acceleration(float acceleration_mm_sec2)
{
_state.acceleration = (_state.max_acceleration == 0) ?
m_state.acceleration = (m_state.max_acceleration == 0) ?
acceleration_mm_sec2 :
// Clamp the acceleration with the maximum.
std::min(_state.max_acceleration, acceleration_mm_sec2);
std::min(m_state.max_acceleration, acceleration_mm_sec2);
}
float GCodeTimeEstimator::get_acceleration() const
{
return _state.acceleration;
return m_state.acceleration;
}
void GCodeTimeEstimator::set_max_acceleration(float acceleration_mm_sec2)
{
_state.max_acceleration = acceleration_mm_sec2;
m_state.max_acceleration = acceleration_mm_sec2;
if (acceleration_mm_sec2 > 0)
_state.acceleration = acceleration_mm_sec2;
m_state.acceleration = acceleration_mm_sec2;
}
float GCodeTimeEstimator::get_max_acceleration() const
{
return _state.max_acceleration;
return m_state.max_acceleration;
}
void GCodeTimeEstimator::set_retract_acceleration(float acceleration_mm_sec2)
{
_state.retract_acceleration = acceleration_mm_sec2;
m_state.retract_acceleration = acceleration_mm_sec2;
}
float GCodeTimeEstimator::get_retract_acceleration() const
{
return _state.retract_acceleration;
return m_state.retract_acceleration;
}
void GCodeTimeEstimator::set_minimum_feedrate(float feedrate_mm_sec)
{
_state.minimum_feedrate = feedrate_mm_sec;
m_state.minimum_feedrate = feedrate_mm_sec;
}
float GCodeTimeEstimator::get_minimum_feedrate() const
{
return _state.minimum_feedrate;
return m_state.minimum_feedrate;
}
void GCodeTimeEstimator::set_minimum_travel_feedrate(float feedrate_mm_sec)
{
_state.minimum_travel_feedrate = feedrate_mm_sec;
m_state.minimum_travel_feedrate = feedrate_mm_sec;
}
float GCodeTimeEstimator::get_minimum_travel_feedrate() const
{
return _state.minimum_travel_feedrate;
return m_state.minimum_travel_feedrate;
}
void GCodeTimeEstimator::set_filament_load_times(const std::vector<double> &filament_load_times)
{
_state.filament_load_times.clear();
m_state.filament_load_times.clear();
for (double t : filament_load_times)
_state.filament_load_times.push_back((float)t);
m_state.filament_load_times.push_back((float)t);
}
void GCodeTimeEstimator::set_filament_unload_times(const std::vector<double> &filament_unload_times)
{
_state.filament_unload_times.clear();
m_state.filament_unload_times.clear();
for (double t : filament_unload_times)
_state.filament_unload_times.push_back((float)t);
m_state.filament_unload_times.push_back((float)t);
}
float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder)
{
return
(_state.filament_load_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
(m_state.filament_load_times.empty() || id_extruder == m_state.extruder_id_unloaded) ?
0 :
(_state.filament_load_times.size() <= id_extruder) ?
_state.filament_load_times.front() :
_state.filament_load_times[id_extruder];
(m_state.filament_load_times.size() <= id_extruder) ?
m_state.filament_load_times.front() :
m_state.filament_load_times[id_extruder];
}
float GCodeTimeEstimator::get_filament_unload_time(unsigned int id_extruder)
{
return
(_state.filament_unload_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
(m_state.filament_unload_times.empty() || id_extruder == m_state.extruder_id_unloaded) ?
0 :
(_state.filament_unload_times.size() <= id_extruder) ?
_state.filament_unload_times.front() :
_state.filament_unload_times[id_extruder];
(m_state.filament_unload_times.size() <= id_extruder) ?
m_state.filament_unload_times.front() :
m_state.filament_unload_times[id_extruder];
}
void GCodeTimeEstimator::set_extrude_factor_override_percentage(float percentage)
{
_state.extrude_factor_override_percentage = percentage;
m_state.extrude_factor_override_percentage = percentage;
}
float GCodeTimeEstimator::get_extrude_factor_override_percentage() const
{
return _state.extrude_factor_override_percentage;
return m_state.extrude_factor_override_percentage;
}
void GCodeTimeEstimator::set_dialect(GCodeFlavor dialect)
{
_state.dialect = dialect;
m_state.dialect = dialect;
}
GCodeFlavor GCodeTimeEstimator::get_dialect() const
{
PROFILE_FUNC();
return _state.dialect;
return m_state.dialect;
}
void GCodeTimeEstimator::set_units(GCodeTimeEstimator::EUnits units)
{
_state.units = units;
m_state.units = units;
}
GCodeTimeEstimator::EUnits GCodeTimeEstimator::get_units() const
{
return _state.units;
return m_state.units;
}
void GCodeTimeEstimator::set_global_positioning_type(GCodeTimeEstimator::EPositioningType type)
{
_state.global_positioning_type = type;
m_state.global_positioning_type = type;
}
GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_global_positioning_type() const
{
return _state.global_positioning_type;
return m_state.global_positioning_type;
}
void GCodeTimeEstimator::set_e_local_positioning_type(GCodeTimeEstimator::EPositioningType type)
{
_state.e_local_positioning_type = type;
m_state.e_local_positioning_type = type;
}
GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_e_local_positioning_type() const
{
return _state.e_local_positioning_type;
return m_state.e_local_positioning_type;
}
int GCodeTimeEstimator::get_g1_line_id() const
{
return _state.g1_line_id;
return m_state.g1_line_id;
}
void GCodeTimeEstimator::increment_g1_line_id()
{
++_state.g1_line_id;
++m_state.g1_line_id;
}
void GCodeTimeEstimator::reset_g1_line_id()
{
_state.g1_line_id = 0;
m_state.g1_line_id = 0;
}
void GCodeTimeEstimator::set_extruder_id(unsigned int id)
{
_state.extruder_id = id;
m_state.extruder_id = id;
}
unsigned int GCodeTimeEstimator::get_extruder_id() const
{
return _state.extruder_id;
return m_state.extruder_id;
}
void GCodeTimeEstimator::reset_extruder_id()
{
// Set the initial extruder ID to unknown. For the multi-material setup it means
// that all the filaments are parked in the MMU and no filament is loaded yet.
_state.extruder_id = _state.extruder_id_unloaded;
m_state.extruder_id = m_state.extruder_id_unloaded;
}
void GCodeTimeEstimator::add_additional_time(float timeSec)
{
PROFILE_FUNC();
_state.additional_time += timeSec;
m_state.additional_time += timeSec;
}
void GCodeTimeEstimator::set_additional_time(float timeSec)
{
_state.additional_time = timeSec;
m_state.additional_time = timeSec;
}
float GCodeTimeEstimator::get_additional_time() const
{
return _state.additional_time;
return m_state.additional_time;
}
void GCodeTimeEstimator::set_default()
@ -648,8 +660,8 @@ namespace Slic3r {
set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]);
}
_state.filament_load_times.clear();
_state.filament_unload_times.clear();
m_state.filament_load_times.clear();
m_state.filament_unload_times.clear();
}
void GCodeTimeEstimator::reset()
@ -664,7 +676,7 @@ namespace Slic3r {
float GCodeTimeEstimator::get_time() const
{
return _time;
return m_time;
}
std::string GCodeTimeEstimator::get_time_dhms() const
@ -677,19 +689,44 @@ namespace Slic3r {
return _get_time_minutes(get_time());
}
std::vector<float> GCodeTimeEstimator::get_color_times() const
{
return m_color_times;
}
std::vector<std::string> GCodeTimeEstimator::get_color_times_dhms() const
{
std::vector<std::string> ret;
for (float t : m_color_times)
{
ret.push_back(_get_time_dhms(t));
}
return ret;
}
std::vector<std::string> GCodeTimeEstimator::get_color_times_minutes() const
{
std::vector<std::string> ret;
for (float t : m_color_times)
{
ret.push_back(_get_time_minutes(t));
}
return ret;
}
// Return an estimate of the memory consumed by the time estimator.
size_t GCodeTimeEstimator::memory_used() const
{
size_t out = sizeof(*this);
out += SLIC3R_STDVEC_MEMSIZE(this->_blocks, Block);
out += SLIC3R_STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId);
out += SLIC3R_STDVEC_MEMSIZE(this->m_blocks, Block);
out += SLIC3R_STDVEC_MEMSIZE(this->m_g1_line_ids, G1LineIdToBlockId);
return out;
}
void GCodeTimeEstimator::_reset()
{
_curr.reset();
_prev.reset();
m_curr.reset();
m_prev.reset();
set_axis_position(X, 0.0f);
set_axis_position(Y, 0.0f);
@ -701,19 +738,23 @@ namespace Slic3r {
reset_extruder_id();
reset_g1_line_id();
_g1_line_ids.clear();
m_g1_line_ids.clear();
_last_st_synchronized_block_id = -1;
m_last_st_synchronized_block_id = -1;
m_needs_color_times = false;
m_color_times.clear();
m_color_time_cache = 0.0f;
}
void GCodeTimeEstimator::_reset_time()
{
_time = 0.0f;
m_time = 0.0f;
}
void GCodeTimeEstimator::_reset_blocks()
{
_blocks.clear();
m_blocks.clear();
}
void GCodeTimeEstimator::_calculate_time()
@ -723,35 +764,32 @@ namespace Slic3r {
_reverse_pass();
_recalculate_trapezoids();
_time += get_additional_time();
m_time += get_additional_time();
m_color_time_cache += get_additional_time();
for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size(); ++i)
{
Block& block = _blocks[i];
#if ENABLE_MOVE_STATS
Block& block = m_blocks[i];
float block_time = 0.0f;
block_time += block.acceleration_time();
block_time += block.cruise_time();
block_time += block.deceleration_time();
_time += block_time;
block.elapsed_time = _time;
m_time += block_time;
block.elapsed_time = m_time;
#if ENABLE_MOVE_STATS
MovesStatsMap::iterator it = _moves_stats.find(block.move_type);
if (it == _moves_stats.end())
it = _moves_stats.insert(MovesStatsMap::value_type(block.move_type, MoveStats())).first;
it->second.count += 1;
it->second.time += block_time;
#else
_time += block.acceleration_time();
_time += block.cruise_time();
_time += block.deceleration_time();
block.elapsed_time = _time;
#endif // ENABLE_MOVE_STATS
m_color_time_cache += block_time;
}
_last_st_synchronized_block_id = (int)_blocks.size() - 1;
m_last_st_synchronized_block_id = (int)m_blocks.size() - 1;
// The additional time has been consumed (added to the total time), reset it to zero.
set_additional_time(0.);
}
@ -866,6 +904,11 @@ namespace Slic3r {
_processM566(line);
break;
}
case 600: // Set color change
{
_processM600(line);
break;
}
case 702: // MK3 MMU2: Process the final filament unload.
{
_processM702(line);
@ -934,7 +977,7 @@ namespace Slic3r {
return;
// calculates block feedrate
_curr.feedrate = std::max(get_feedrate(), block.is_travel_move() ? get_minimum_travel_feedrate() : get_minimum_feedrate());
m_curr.feedrate = std::max(get_feedrate(), block.is_travel_move() ? get_minimum_travel_feedrate() : get_minimum_feedrate());
float distance = block.move_length();
float invDistance = 1.0f / distance;
@ -942,23 +985,23 @@ namespace Slic3r {
float min_feedrate_factor = 1.0f;
for (unsigned char a = X; a < Num_Axis; ++a)
{
_curr.axis_feedrate[a] = _curr.feedrate * block.delta_pos[a] * invDistance;
m_curr.axis_feedrate[a] = m_curr.feedrate * block.delta_pos[a] * invDistance;
if (a == E)
_curr.axis_feedrate[a] *= get_extrude_factor_override_percentage();
m_curr.axis_feedrate[a] *= get_extrude_factor_override_percentage();
_curr.abs_axis_feedrate[a] = std::abs(_curr.axis_feedrate[a]);
if (_curr.abs_axis_feedrate[a] > 0.0f)
min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / _curr.abs_axis_feedrate[a]);
m_curr.abs_axis_feedrate[a] = std::abs(m_curr.axis_feedrate[a]);
if (m_curr.abs_axis_feedrate[a] > 0.0f)
min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / m_curr.abs_axis_feedrate[a]);
}
block.feedrate.cruise = min_feedrate_factor * _curr.feedrate;
block.feedrate.cruise = min_feedrate_factor * m_curr.feedrate;
if (min_feedrate_factor < 1.0f)
{
for (unsigned char a = X; a < Num_Axis; ++a)
{
_curr.axis_feedrate[a] *= min_feedrate_factor;
_curr.abs_axis_feedrate[a] *= min_feedrate_factor;
m_curr.axis_feedrate[a] *= min_feedrate_factor;
m_curr.abs_axis_feedrate[a] *= min_feedrate_factor;
}
}
@ -975,25 +1018,25 @@ namespace Slic3r {
block.acceleration = acceleration;
// calculates block exit feedrate
_curr.safe_feedrate = block.feedrate.cruise;
m_curr.safe_feedrate = block.feedrate.cruise;
for (unsigned char a = X; a < Num_Axis; ++a)
{
float axis_max_jerk = get_axis_max_jerk((EAxis)a);
if (_curr.abs_axis_feedrate[a] > axis_max_jerk)
_curr.safe_feedrate = std::min(_curr.safe_feedrate, axis_max_jerk);
if (m_curr.abs_axis_feedrate[a] > axis_max_jerk)
m_curr.safe_feedrate = std::min(m_curr.safe_feedrate, axis_max_jerk);
}
block.feedrate.exit = _curr.safe_feedrate;
block.feedrate.exit = m_curr.safe_feedrate;
// calculates block entry feedrate
float vmax_junction = _curr.safe_feedrate;
if (!_blocks.empty() && (_prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD))
float vmax_junction = m_curr.safe_feedrate;
if (!m_blocks.empty() && (m_prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD))
{
bool prev_speed_larger = _prev.feedrate > block.feedrate.cruise;
float smaller_speed_factor = prev_speed_larger ? (block.feedrate.cruise / _prev.feedrate) : (_prev.feedrate / block.feedrate.cruise);
bool prev_speed_larger = m_prev.feedrate > block.feedrate.cruise;
float smaller_speed_factor = prev_speed_larger ? (block.feedrate.cruise / m_prev.feedrate) : (m_prev.feedrate / block.feedrate.cruise);
// Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
vmax_junction = prev_speed_larger ? block.feedrate.cruise : _prev.feedrate;
vmax_junction = prev_speed_larger ? block.feedrate.cruise : m_prev.feedrate;
float v_factor = 1.0f;
bool limited = false;
@ -1001,8 +1044,8 @@ namespace Slic3r {
for (unsigned char a = X; a < Num_Axis; ++a)
{
// Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop.
float v_exit = _prev.axis_feedrate[a];
float v_entry = _curr.axis_feedrate[a];
float v_exit = m_prev.axis_feedrate[a];
float v_entry = m_curr.axis_feedrate[a];
if (prev_speed_larger)
v_exit *= smaller_speed_factor;
@ -1044,23 +1087,23 @@ namespace Slic3r {
float vmax_junction_threshold = vmax_junction * 0.99f;
// Not coasting. The machine will stop and start the movements anyway, better to start the segment from start.
if ((_prev.safe_feedrate > vmax_junction_threshold) && (_curr.safe_feedrate > vmax_junction_threshold))
vmax_junction = _curr.safe_feedrate;
if ((m_prev.safe_feedrate > vmax_junction_threshold) && (m_curr.safe_feedrate > vmax_junction_threshold))
vmax_junction = m_curr.safe_feedrate;
}
float v_allowable = Block::max_allowable_speed(-acceleration, _curr.safe_feedrate, distance);
float v_allowable = Block::max_allowable_speed(-acceleration, m_curr.safe_feedrate, distance);
block.feedrate.entry = std::min(vmax_junction, v_allowable);
block.max_entry_speed = vmax_junction;
block.flags.nominal_length = (block.feedrate.cruise <= v_allowable);
block.flags.recalculate = true;
block.safe_feedrate = _curr.safe_feedrate;
block.safe_feedrate = m_curr.safe_feedrate;
// calculates block trapezoid
block.calculate_trapezoid();
// updates previous
_prev = _curr;
m_prev = m_curr;
// updates axis positions
for (unsigned char a = X; a < Num_Axis; ++a)
@ -1091,8 +1134,8 @@ namespace Slic3r {
#endif // ENABLE_MOVE_STATS
// adds block to blocks list
_blocks.emplace_back(block);
_g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1));
m_blocks.emplace_back(block);
m_g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)m_blocks.size() - 1));
}
void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line)
@ -1336,6 +1379,18 @@ namespace Slic3r {
set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC);
}
void GCodeTimeEstimator::_processM600(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
m_needs_color_times = true;
_calculate_time();
if (m_color_time_cache != 0.0f)
{
m_color_times.push_back(m_color_time_cache);
m_color_time_cache = 0.0f;
}
}
void GCodeTimeEstimator::_processM702(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
@ -1376,11 +1431,11 @@ namespace Slic3r {
void GCodeTimeEstimator::_forward_pass()
{
PROFILE_FUNC();
if (_blocks.size() > 1)
if (m_blocks.size() > 1)
{
for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size() - 1; ++i)
for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size() - 1; ++i)
{
_planner_forward_pass_kernel(_blocks[i], _blocks[i + 1]);
_planner_forward_pass_kernel(m_blocks[i], m_blocks[i + 1]);
}
}
}
@ -1388,11 +1443,11 @@ namespace Slic3r {
void GCodeTimeEstimator::_reverse_pass()
{
PROFILE_FUNC();
if (_blocks.size() > 1)
if (m_blocks.size() > 1)
{
for (int i = (int)_blocks.size() - 1; i >= _last_st_synchronized_block_id + 2; --i)
for (int i = (int)m_blocks.size() - 1; i >= m_last_st_synchronized_block_id + 2; --i)
{
_planner_reverse_pass_kernel(_blocks[i - 1], _blocks[i]);
_planner_reverse_pass_kernel(m_blocks[i - 1], m_blocks[i]);
}
}
}
@ -1444,9 +1499,9 @@ namespace Slic3r {
Block* curr = nullptr;
Block* next = nullptr;
for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size(); ++i)
{
Block& b = _blocks[i];
Block& b = m_blocks[i];
curr = next;
next = &b;
@ -1517,7 +1572,7 @@ namespace Slic3r {
{
std::cout << MOVE_TYPE_STR[move.first];
std::cout << ": count " << move.second.count << " (" << 100.0f * (float)move.second.count / moves_count << "%)";
std::cout << " - time: " << move.second.time << "s (" << 100.0f * move.second.time / _time << "%)";
std::cout << " - time: " << move.second.time << "s (" << 100.0f * move.second.time / m_time << "%)";
std::cout << std::endl;
}
std::cout << std::endl;

View file

@ -215,17 +215,22 @@ namespace Slic3r {
typedef std::vector<G1LineIdToBlockId> G1LineIdToBlockIdMap;
private:
EMode _mode;
GCodeReader _parser;
State _state;
Feedrates _curr;
Feedrates _prev;
BlocksList _blocks;
EMode m_mode;
GCodeReader m_parser;
State m_state;
Feedrates m_curr;
Feedrates m_prev;
BlocksList m_blocks;
// Map between g1 line id and blocks id, used to speed up export of remaining times
G1LineIdToBlockIdMap _g1_line_ids;
G1LineIdToBlockIdMap m_g1_line_ids;
// Index of the last block already st_synchronized
int _last_st_synchronized_block_id;
float _time; // s
int m_last_st_synchronized_block_id;
float m_time; // s
// data to calculate color print times
bool m_needs_color_times;
std::vector<float> m_color_times;
float m_color_time_cache;
#if ENABLE_MOVE_STATS
MovesStatsMap _moves_stats;
@ -341,6 +346,15 @@ namespace Slic3r {
// Returns the estimated time, in minutes (integer)
std::string get_time_minutes() const;
// Returns the estimated time, in seconds, for each color
std::vector<float> get_color_times() const;
// Returns the estimated time, in format DDd HHh MMm SSs, for each color
std::vector<std::string> get_color_times_dhms() const;
// Returns the estimated time, in minutes (integer), for each color
std::vector<std::string> get_color_times_minutes() const;
// Return an estimate of the memory consumed by the time estimator.
size_t memory_used() const;
@ -409,6 +423,9 @@ namespace Slic3r {
// Set allowable instantaneous speed change
void _processM566(const GCodeReader::GCodeLine& line);
// Set color change
void _processM600(const GCodeReader::GCodeLine& line);
// Unload the current filament into the MK3 MMU2 unit at the end of print.
void _processM702(const GCodeReader::GCodeLine& line);

View file

@ -1,11 +1,13 @@
#ifndef MTUTILS_HPP
#define MTUTILS_HPP
#include <atomic> // for std::atomic_flag and memory orders
#include <mutex> // for std::lock_guard
#include <functional> // for std::function
#include <utility> // for std::forward
#include <atomic> // for std::atomic_flag and memory orders
#include <mutex> // for std::lock_guard
#include <functional> // for std::function
#include <utility> // for std::forward
#include <vector>
#include <algorithm>
#include <cmath>
#include "libslic3r.h"
#include "Point.hpp"
@ -242,6 +244,58 @@ template<class C> bool all_of(const C &container)
});
}
template<class T> struct remove_cvref
{
using type =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
};
template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
template<template<class> class C, class T>
class Container : public C<remove_cvref_t<T>>
{
public:
explicit Container(size_t count, T &&initval)
: C<remove_cvref_t<T>>(count, initval)
{}
};
template<class T> using DefaultContainer = std::vector<T>;
/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
template<class T, class I, template<class> class C = DefaultContainer>
inline C<remove_cvref_t<T>> linspace(const T &start, const T &stop, const I &n)
{
Container<C, T> vals(n, T());
T stride = (stop - start) / n;
size_t i = 0;
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
return start + i++ * stride;
});
return vals;
}
/// A set of equidistant values starting from 'start' (inclusive), ending
/// in the closest multiple of 'stride' less than or equal to 'end' and
/// leaving 'stride' space between each value.
/// Very similar to Matlab [start:stride:end] notation.
template<class T, template<class> class C = DefaultContainer>
inline C<remove_cvref_t<T>> grid(const T &start, const T &stop, const T &stride)
{
Container<C, T> vals(size_t(std::ceil((stop - start) / stride)), T());
int i = 0;
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
return start + i++ * stride;
});
return vals;
}
// A shorter C++14 style form of the enable_if metafunction
template<bool B, class T>
using enable_if_t = typename std::enable_if<B, T>::type;
@ -304,7 +358,7 @@ inline SLIC3R_CONSTEXPR ScaledCoordOnly<Tout> scaled(const Tin &v) SLIC3R_NOEXCE
template<class Tout = coord_t, class Tin, int N, class = FloatingOnly<Tin>>
inline EigenVec<ArithmeticOnly<Tout>, N> scaled(const EigenVec<Tin, N> &v)
{
return v.template cast<Tout>() / SCALING_FACTOR;
return (v / SCALING_FACTOR).template cast<Tout>();
}
// Conversion from arithmetic scaled type to floating point unscaled

View file

@ -241,6 +241,8 @@ struct PrintStatistics
PrintStatistics() { clear(); }
std::string estimated_normal_print_time;
std::string estimated_silent_print_time;
std::vector<std::string> estimated_normal_color_print_times;
std::vector<std::string> estimated_silent_color_print_times;
double total_used_filament;
double total_extruded_volume;
double total_cost;
@ -259,6 +261,8 @@ struct PrintStatistics
void clear() {
estimated_normal_print_time.clear();
estimated_silent_print_time.clear();
estimated_normal_color_print_times.clear();
estimated_silent_color_print_times.clear();
total_used_filament = 0.;
total_extruded_volume = 0.;
total_cost = 0.;

View file

@ -2504,6 +2504,19 @@ void PrintConfigDef::init_sla_params()
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1.0));
def = this->add("support_base_safety_distance", coFloat);
def->label = L("Support base safety distance");
def->category = L("Supports");
def->tooltip = L(
"The minimum distance of the pillar base from the model in mm. "
"Makes sense in zero elevation mode where a gap according "
"to this parameter is inserted between the model and the pad.");
def->sidetext = L("mm");
def->min = 0;
def->max = 10;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(1));
def = this->add("support_critical_angle", coFloat);
def->label = L("Critical angle");
@ -2537,7 +2550,9 @@ void PrintConfigDef::init_sla_params()
def = this->add("support_object_elevation", coFloat);
def->label = L("Object elevation");
def->category = L("Supports");
def->tooltip = L("How much the supports should lift up the supported object.");
def->tooltip = L("How much the supports should lift up the supported object. "
"If this value is zero, the bottom of the model geometry "
"will be considered as part of the pad.");
def->sidetext = L("mm");
def->min = 0;
def->max = 150; // This is the max height of print on SL1
@ -2623,6 +2638,47 @@ void PrintConfigDef::init_sla_params()
def->max = 90;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(45.0));
def = this->add("pad_object_gap", coFloat);
def->label = L("Pad object gap");
def->category = L("Pad");
def->tooltip = L("The gap between the object bottom and the generated "
"pad in zero elevation mode.");
def->sidetext = L("mm");
def->min = 0;
def->max = 10;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(1));
def = this->add("pad_object_connector_stride", coFloat);
def->label = L("Pad object connector stride");
def->category = L("Pad");
def->tooltip = L("Distance between two connector sticks between "
"the object pad and the generated pad.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(10));
def = this->add("pad_object_connector_width", coFloat);
def->label = L("Pad object connector width");
def->category = L("Pad");
def->tooltip = L("The width of the connectors sticks which connect the "
"object pad and the generated pad.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0.5));
def = this->add("pad_object_connector_penetration", coFloat);
def->label = L("Pad object connector penetration");
def->category = L("Pad");
def->tooltip = L(
"How much should the tiny connectors penetrate into the model body.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0.3));
}
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)

View file

@ -983,6 +983,9 @@ public:
// The height of the pillar base cone in mm.
ConfigOptionFloat support_base_height /*= 1.0*/;
// The minimum distance of the pillar base from the model in mm.
ConfigOptionFloat support_base_safety_distance; /*= 1.0*/;
// The default angle for connecting support sticks and junctions.
ConfigOptionFloat support_critical_angle /*= 45*/;
@ -996,7 +999,7 @@ public:
// 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*/;
/////// Following options influence automatic support points placement:
ConfigOptionInt support_points_density_relative;
ConfigOptionFloat support_points_minimal_distance;
@ -1021,6 +1024,26 @@ public:
// The slope of the pad wall...
ConfigOptionFloat pad_wall_slope;
// /////////////////////////////////////////////////////////////////////////
// Zero elevation mode parameters:
// - The object pad will be derived from the the model geometry.
// - There will be a gap between the object pad and the generated pad
// according to the support_base_safety_distance parameter.
// - The two pads will be connected with tiny connector sticks
// /////////////////////////////////////////////////////////////////////////
// This is the gap between the object bottom and the generated pad
ConfigOptionFloat pad_object_gap;
// How far to place the connector sticks on the object pad perimeter
ConfigOptionFloat pad_object_connector_stride;
// The width of the connectors sticks
ConfigOptionFloat pad_object_connector_width;
// How much should the tiny connectors penetrate into the model body
ConfigOptionFloat pad_object_connector_penetration;
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
@ -1038,6 +1061,7 @@ protected:
OPT_PTR(support_pillar_widening_factor);
OPT_PTR(support_base_diameter);
OPT_PTR(support_base_height);
OPT_PTR(support_base_safety_distance);
OPT_PTR(support_critical_angle);
OPT_PTR(support_max_bridge_length);
OPT_PTR(support_max_pillar_link_distance);
@ -1050,6 +1074,10 @@ protected:
OPT_PTR(pad_max_merge_distance);
OPT_PTR(pad_edge_radius);
OPT_PTR(pad_wall_slope);
OPT_PTR(pad_object_gap);
OPT_PTR(pad_object_connector_stride);
OPT_PTR(pad_object_connector_width);
OPT_PTR(pad_object_connector_penetration);
}
};

View file

@ -8,9 +8,9 @@
#include "MTUtils.hpp"
// For debugging:
//#include <fstream>
//#include <libnest2d/tools/benchmark.h>
//#include "SVG.hpp"
// #include <fstream>
// #include <libnest2d/tools/benchmark.h>
// #include "SVG.hpp"
namespace Slic3r { namespace sla {
@ -184,9 +184,10 @@ Contour3D walls(const Polygon& lower, const Polygon& upper,
}
/// Offsetting with clipper and smoothing the edges into a curvature.
void offset(ExPolygon& sh, coord_t distance) {
void offset(ExPolygon& sh, coord_t distance, bool edgerounding = true) {
using ClipperLib::ClipperOffset;
using ClipperLib::jtRound;
using ClipperLib::jtMiter;
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
using ClipperLib::Path;
@ -203,11 +204,13 @@ void offset(ExPolygon& sh, coord_t distance) {
return;
}
auto jointype = edgerounding? jtRound : jtMiter;
ClipperOffset offs;
offs.ArcTolerance = scaled<double>(0.01);
Paths result;
offs.AddPath(ctour, jtRound, etClosedPolygon);
offs.AddPaths(holes, jtRound, etClosedPolygon);
offs.AddPath(ctour, jointype, etClosedPolygon);
offs.AddPaths(holes, jointype, etClosedPolygon);
offs.Execute(result, static_cast<double>(distance));
// Offsetting reverts the orientation and also removes the last vertex
@ -237,6 +240,50 @@ void offset(ExPolygon& sh, coord_t distance) {
}
}
void offset(Polygon &sh, coord_t distance, bool edgerounding = true)
{
using ClipperLib::ClipperOffset;
using ClipperLib::jtRound;
using ClipperLib::jtMiter;
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
using ClipperLib::Path;
auto &&ctour = Slic3rMultiPoint_to_ClipperPath(sh);
// If the input is not at least a triangle, we can not do this algorithm
if (ctour.size() < 3) {
BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
return;
}
ClipperOffset offs;
offs.ArcTolerance = 0.01 * scaled(1.);
Paths result;
offs.AddPath(ctour, edgerounding ? jtRound : jtMiter, etClosedPolygon);
offs.Execute(result, static_cast<double>(distance));
// Offsetting reverts the orientation and also removes the last vertex
// so boost will not have a closed polygon.
bool found_the_contour = false;
for (auto &r : result) {
if (ClipperLib::Orientation(r)) {
// We don't like if the offsetting generates more than one contour
// but throwing would be an overkill. Instead, we should warn the
// caller about the inability to create correct geometries
if (!found_the_contour) {
auto rr = ClipperPath_to_Slic3rPolygon(r);
sh.points.swap(rr.points);
found_the_contour = true;
} else {
BOOST_LOG_TRIVIAL(warning)
<< "Warning: offsetting result is invalid!";
}
}
}
}
/// Unification of polygons (with clipper) preserving holes as well.
ExPolygons unify(const ExPolygons& shapes) {
using ClipperLib::ptSubject;
@ -307,6 +354,116 @@ ExPolygons unify(const ExPolygons& shapes) {
return retv;
}
Polygons unify(const Polygons& shapes) {
using ClipperLib::ptSubject;
bool closed = true;
bool valid = true;
ClipperLib::Clipper clipper;
for(auto& path : shapes) {
auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path);
if(!clipperpath.empty())
valid &= clipper.AddPath(clipperpath, ptSubject, closed);
}
if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!";
ClipperLib::Paths result;
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero);
Polygons ret;
for (ClipperLib::Path &p : result) {
Polygon pp = ClipperPath_to_Slic3rPolygon(p);
if (!pp.is_clockwise()) ret.emplace_back(std::move(pp));
}
return ret;
}
// Function to cut tiny connector cavities for a given polygon. The input poly
// will be offsetted by "padding" and small rectangle shaped cavities will be
// inserted along the perimeter in every "stride" distance. The stick rectangles
// will have a with about "stick_width". The input dimensions are in world
// measure, not the scaled clipper units.
void breakstick_holes(ExPolygon& poly,
double padding,
double stride,
double stick_width,
double penetration)
{
// SVG svg("bridgestick_plate.svg");
// svg.draw(poly);
auto transf = [stick_width, penetration, padding, stride](Points &pts) {
// The connector stick will be a small rectangle with dimensions
// stick_width x (penetration + padding) to have some penetration
// into the input polygon.
Points out;
out.reserve(2 * pts.size()); // output polygon points
// stick bottom and right edge dimensions
double sbottom = scaled(stick_width);
double sright = scaled(penetration + padding);
// scaled stride distance
double sstride = scaled(stride);
double t = 0;
// process pairs of vertices as an edge, start with the last and
// first point
for (size_t i = pts.size() - 1, j = 0; j < pts.size(); i = j, ++j) {
// Get vertices and the direction vectors
const Point &a = pts[i], &b = pts[j];
Vec2d dir = b.cast<double>() - a.cast<double>();
double nrm = dir.norm();
dir /= nrm;
Vec2d dirp(-dir(Y), dir(X));
// Insert start point
out.emplace_back(a);
// dodge the start point, do not make sticks on the joins
while (t < sbottom) t += sbottom;
double tend = nrm - sbottom;
while (t < tend) { // insert the stick on the polygon perimeter
// calculate the stick rectangle vertices and insert them
// into the output.
Point p1 = a + (t * dir).cast<coord_t>();
Point p2 = p1 + (sright * dirp).cast<coord_t>();
Point p3 = p2 + (sbottom * dir).cast<coord_t>();
Point p4 = p3 + (sright * -dirp).cast<coord_t>();
out.insert(out.end(), {p1, p2, p3, p4});
// continue along the perimeter
t += sstride;
}
t = t - nrm;
// Insert edge endpoint
out.emplace_back(b);
}
// move the new points
out.shrink_to_fit();
pts.swap(out);
};
if(stride > 0.0 && stick_width > 0.0 && padding > 0.0) {
transf(poly.contour.points);
for (auto &h : poly.holes) transf(h.points);
}
// svg.draw(poly);
// svg.Close();
}
/// This method will create a rounded edge around a flat polygon in 3d space.
/// 'base_plate' parameter is the target plate.
/// 'radius' is the radius of the edges.
@ -426,41 +583,38 @@ inline Point centroid(Points& pp) {
return c;
}
inline Point centroid(const ExPolygon& poly) {
return poly.contour.centroid();
inline Point centroid(const Polygon& poly) {
return poly.centroid();
}
/// A fake concave hull that is constructed by connecting separate shapes
/// with explicit bridges. Bridges are generated from each shape's centroid
/// to the center of the "scene" which is the centroid calculated from the shape
/// centroids (a star is created...)
ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
ThrowOnCancel throw_on_cancel = [](){})
Polygons concave_hull(const Polygons& polys, double max_dist_mm = 50,
ThrowOnCancel throw_on_cancel = [](){})
{
namespace bgi = boost::geometry::index;
using SpatElement = std::pair<BoundingBox, unsigned>;
using SpatElement = std::pair<Point, unsigned>;
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
if(polys.empty()) return ExPolygons();
if(polys.empty()) return Polygons();
const double max_dist = scaled(max_dist_mm);
ExPolygons punion = unify(polys); // could be redundant
Polygons punion = unify(polys); // could be redundant
if(punion.size() == 1) return punion;
// We get the centroids of all the islands in the 2D slice
Points centroids; centroids.reserve(punion.size());
std::transform(punion.begin(), punion.end(), std::back_inserter(centroids),
[](const ExPolygon& poly) { return centroid(poly); });
SpatIndex boxindex; unsigned idx = 0;
std::for_each(punion.begin(), punion.end(),
[&boxindex, &idx](const ExPolygon& expo) {
BoundingBox bb(expo);
boxindex.insert(std::make_pair(bb, idx++));
});
[](const Polygon& poly) { return centroid(poly); });
SpatIndex ctrindex;
unsigned idx = 0;
for(const Point &ct : centroids) ctrindex.insert(std::make_pair(ct, idx++));
// Centroid of the centroids of islands. This is where the additional
// connector sticks are routed.
Point cc = centroid(centroids);
@ -470,25 +624,32 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
idx = 0;
std::transform(centroids.begin(), centroids.end(),
std::back_inserter(punion),
[&punion, &boxindex, cc, max_dist_mm, &idx, throw_on_cancel]
[&centroids, &ctrindex, cc, max_dist, &idx, throw_on_cancel]
(const Point& c)
{
throw_on_cancel();
double dx = x(c) - x(cc), dy = y(c) - y(cc);
double l = std::sqrt(dx * dx + dy * dy);
double nx = dx / l, ny = dy / l;
double max_dist = scaled<double>(max_dist_mm);
ExPolygon& expo = punion[idx++];
BoundingBox querybb(expo);
querybb.offset(max_dist);
Point& ct = centroids[idx];
std::vector<SpatElement> result;
boxindex.query(bgi::intersects(querybb), std::back_inserter(result));
if(result.size() <= 1) return ExPolygon();
ctrindex.query(bgi::nearest(ct, 2), std::back_inserter(result));
ExPolygon r;
auto& ctour = r.contour.points;
double dist = max_dist;
for (const SpatElement &el : result)
if (el.second != idx) {
dist = Line(el.first, ct).length();
break;
}
idx++;
if (dist >= max_dist) return Polygon();
Polygon r;
auto& ctour = r.points;
ctour.reserve(3);
ctour.emplace_back(cc);
@ -507,24 +668,20 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
return punion;
}
void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
float layerh, ThrowOnCancel thrfn)
void base_plate(const TriangleMesh & mesh,
ExPolygons & output,
const std::vector<float> &heights,
ThrowOnCancel thrfn)
{
TriangleMesh m = mesh;
m.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer slicer(&m);
auto bb = mesh.bounding_box();
float gnd = float(bb.min(Z));
std::vector<float> heights = {float(bb.min(Z))};
for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh)
heights.emplace_back(hi);
std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
if (mesh.empty()) return;
// m.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer slicer(&mesh);
std::vector<ExPolygons> out; out.reserve(heights.size());
slicer.slice(heights, 0.f, &out, thrfn);
size_t count = 0; for(auto& o : out) count += o.size();
// Now we have to unify all slice layers which can be an expensive operation
// so we will try to simplify the polygons
ExPolygons tmp; tmp.reserve(count);
@ -533,16 +690,33 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
auto&& exss = e.simplify(scaled<double>(0.1));
for(ExPolygon& ep : exss) tmp.emplace_back(std::move(ep));
}
ExPolygons utmp = unify(tmp);
for(auto& o : utmp) {
auto&& smp = o.simplify(scaled<double>(0.1));
output.insert(output.end(), smp.begin(), smp.end());
}
}
Contour3D create_base_pool(const ExPolygons &ground_layer,
void base_plate(const TriangleMesh &mesh,
ExPolygons & output,
float h,
float layerh,
ThrowOnCancel thrfn)
{
auto bb = mesh.bounding_box();
float gnd = float(bb.min(Z));
std::vector<float> heights = {float(bb.min(Z))};
for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh)
heights.emplace_back(hi);
base_plate(mesh, output, heights, thrfn);
}
Contour3D create_base_pool(const Polygons &ground_layer,
const ExPolygons &obj_self_pad = {},
const PoolConfig& cfg = PoolConfig())
{
// for debugging:
@ -557,7 +731,7 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
// serve as the bottom plate of the pad. We will offset this concave hull
// and then offset back the result with clipper with rounding edges ON. This
// trick will create a nice rounded pad shape.
ExPolygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
Polygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
const double thickness = cfg.min_wall_thickness_mm;
const double wingheight = cfg.min_wall_height_mm;
@ -577,42 +751,37 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
Contour3D pool;
for(ExPolygon& concaveh : concavehs) {
if(concaveh.contour.points.empty()) return pool;
// Get rid of any holes in the concave hull output.
concaveh.holes.clear();
for(Polygon& concaveh : concavehs) {
if(concaveh.points.empty()) return pool;
// Here lies the trick that does the smoothing only with clipper offset
// calls. The offset is configured to round edges. Inner edges will
// be rounded because we offset twice: ones to get the outer (top) plate
// and again to get the inner (bottom) plate
auto outer_base = concaveh;
outer_base.holes.clear();
offset(outer_base, s_safety_dist + s_wingdist + s_thickness);
ExPolygon bottom_poly = outer_base;
bottom_poly.holes.clear();
ExPolygon bottom_poly; bottom_poly.contour = outer_base;
offset(bottom_poly, -s_bottom_offs);
// Punching a hole in the top plate for the cavity
ExPolygon top_poly;
ExPolygon middle_base;
ExPolygon inner_base;
top_poly.contour = outer_base.contour;
top_poly.contour = outer_base;
if(wingheight > 0) {
inner_base = outer_base;
inner_base.contour = outer_base;
offset(inner_base, -(s_thickness + s_wingdist + s_eradius));
middle_base = outer_base;
middle_base.contour = outer_base;
offset(middle_base, -s_thickness);
top_poly.holes.emplace_back(middle_base.contour);
auto& tph = top_poly.holes.back().points;
std::reverse(tph.begin(), tph.end());
}
ExPolygon ob = outer_base; double wh = 0;
ExPolygon ob; ob.contour = outer_base; double wh = 0;
// now we will calculate the angle or portion of the circle from
// pi/2 that will connect perfectly with the bottom plate.
@ -659,6 +828,7 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
if(wingheight > 0) {
// Generate the smoothed edge geometry
wh = 0;
ob = middle_base;
if(s_eradius) pool.merge(round_edges(middle_base,
r,
phi - 90, // from tangent lines
@ -673,11 +843,59 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
wh, -wingdist, thrcl));
}
// Now we need to triangulate the top and bottom plates as well as the
// cavity bottom plate which is the same as the bottom plate but it is
// elevated by the thickness.
if (cfg.embed_object) {
ExPolygons bttms = diff_ex(to_polygons(bottom_poly),
to_polygons(obj_self_pad));
assert(!bttms.empty());
std::sort(bttms.begin(), bttms.end(),
[](const ExPolygon& e1, const ExPolygon& e2) {
return e1.contour.area() > e2.contour.area();
});
if(wingheight > 0) inner_base.holes = bttms.front().holes;
else top_poly.holes = bttms.front().holes;
auto straight_walls =
[&pool](const Polygon &cntr, coord_t z_low, coord_t z_high) {
auto lines = cntr.lines();
for (auto &l : lines) {
auto s = coord_t(pool.points.size());
auto& pts = pool.points;
pts.emplace_back(unscale(l.a.x(), l.a.y(), z_low));
pts.emplace_back(unscale(l.b.x(), l.b.y(), z_low));
pts.emplace_back(unscale(l.a.x(), l.a.y(), z_high));
pts.emplace_back(unscale(l.b.x(), l.b.y(), z_high));
pool.indices.emplace_back(s, s + 1, s + 3);
pool.indices.emplace_back(s, s + 3, s + 2);
}
};
coord_t z_lo = -scaled(fullheight), z_hi = -scaled(wingheight);
for (ExPolygon &ep : bttms) {
pool.merge(triangulate_expolygon_3d(ep, -fullheight, true));
for (auto &h : ep.holes) straight_walls(h, z_lo, z_hi);
}
// Skip the outer contour, triangulate the holes
for (auto it = std::next(bttms.begin()); it != bttms.end(); ++it) {
pool.merge(triangulate_expolygon_3d(*it, -wingheight));
straight_walls(it->contour, z_lo, z_hi);
}
} else {
// Now we need to triangulate the top and bottom plates as well as
// the cavity bottom plate which is the same as the bottom plate
// but it is elevated by the thickness.
pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
}
pool.merge(triangulate_expolygon_3d(top_poly));
pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
if(wingheight > 0)
pool.merge(triangulate_expolygon_3d(inner_base, -wingheight));
@ -687,8 +905,8 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
return pool;
}
void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
const PoolConfig& cfg)
void create_base_pool(const Polygons &ground_layer, TriangleMesh& out,
const ExPolygons &holes, const PoolConfig& cfg)
{
@ -698,7 +916,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
// std::fstream fout("pad_debug.obj", std::fstream::out);
// if(fout.good()) pool.to_obj(fout);
out.merge(mesh(create_base_pool(ground_layer, cfg)));
out.merge(mesh(create_base_pool(ground_layer, holes, cfg)));
}
}

View file

@ -8,7 +8,9 @@
namespace Slic3r {
class ExPolygon;
class Polygon;
using ExPolygons = std::vector<ExPolygon>;
using Polygons = std::vector<Polygon>;
class TriangleMesh;
@ -19,16 +21,40 @@ using ThrowOnCancel = std::function<void(void)>;
/// Calculate the polygon representing the silhouette from the specified height
void base_plate(const TriangleMesh& mesh, // input mesh
ExPolygons& output, // Output will be merged with
float zlevel = 0.1f, // Plate creation level
float samplingheight = 0.1f, // The height range to sample
float layerheight = 0.05f, // The sampling height
ThrowOnCancel thrfn = [](){}); // Will be called frequently
void base_plate(const TriangleMesh& mesh, // input mesh
ExPolygons& output, // Output will be merged with
const std::vector<float>&, // Exact Z levels to sample
ThrowOnCancel thrfn = [](){}); // Will be called frequently
// Function to cut tiny connector cavities for a given polygon. The input poly
// will be offsetted by "padding" and small rectangle shaped cavities will be
// inserted along the perimeter in every "stride" distance. The stick rectangles
// will have a with about "stick_width". The input dimensions are in world
// measure, not the scaled clipper units.
void breakstick_holes(ExPolygon &poly,
double padding,
double stride,
double stick_width,
double penetration = 0.0);
struct PoolConfig {
double min_wall_thickness_mm = 2;
double min_wall_height_mm = 5;
double max_merge_distance_mm = 50;
double edge_radius_mm = 1;
double wall_slope = std::atan(1.0); // Universal constant for Pi/4
struct EmbedObject {
double object_gap_mm = 0.5;
double stick_stride_mm = 10;
double stick_width_mm = 0.3;
double stick_penetration_mm = 0.1;
bool enabled = false;
operator bool() const { return enabled; }
} embed_object;
ThrowOnCancel throw_on_cancel = [](){};
@ -42,15 +68,12 @@ struct PoolConfig {
};
/// Calculate the pool for the mesh for SLA printing
void create_base_pool(const ExPolygons& base_plate,
void create_base_pool(const Polygons& base_plate,
TriangleMesh& output_mesh,
const ExPolygons& holes,
const PoolConfig& = PoolConfig());
/// TODO: Currently the base plate of the pool will have half the height of the
/// whole pool. So the carved out space has also half the height. This is not
/// a particularly elegant solution, the thickness should be exactly
/// min_wall_thickness and it should be corrected in the future. This method
/// will return the correct value for further processing.
/// Returns the elevation needed for compensating the pad.
inline double get_pad_elevation(const PoolConfig& cfg) {
return cfg.min_wall_thickness_mm;
}

View file

@ -60,7 +60,7 @@ class EigenMesh3D {
Eigen::MatrixXd m_V;
Eigen::MatrixXi m_F;
double m_ground_level = 0;
double m_ground_level = 0, m_gnd_offset = 0;
std::unique_ptr<AABBImpl> m_aabb;
public:
@ -71,7 +71,9 @@ public:
~EigenMesh3D();
inline double ground_level() const { return m_ground_level; }
inline double ground_level() const { return m_ground_level + m_gnd_offset; }
inline void ground_level_offset(double o) { m_gnd_offset = o; }
inline double ground_level_offset() const { return m_gnd_offset; }
inline const Eigen::MatrixXd& V() const { return m_V; }
inline const Eigen::MatrixXi& F() const { return m_F; }
@ -149,6 +151,12 @@ public:
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
double squared_distance(const Vec3d& p, int& i, Vec3d& c) const;
inline double squared_distance(const Vec3d &p) const
{
int i;
Vec3d c;
return squared_distance(p, i, c);
}
};

View file

@ -7,13 +7,15 @@
#include <Eigen/Geometry>
#include <libslic3r/BoundingBox.hpp>
namespace Slic3r {
namespace sla {
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d;
using SpatElement = std::pair<Vec3d, unsigned>;
using PointIndexEl = std::pair<Vec3d, unsigned>;
class SpatIndex {
class PointIndex {
class Impl;
// We use Pimpl because it takes a long time to compile boost headers which
@ -21,30 +23,67 @@ class SpatIndex {
std::unique_ptr<Impl> m_impl;
public:
SpatIndex();
~SpatIndex();
PointIndex();
~PointIndex();
SpatIndex(const SpatIndex&);
SpatIndex(SpatIndex&&);
SpatIndex& operator=(const SpatIndex&);
SpatIndex& operator=(SpatIndex&&);
PointIndex(const PointIndex&);
PointIndex(PointIndex&&);
PointIndex& operator=(const PointIndex&);
PointIndex& operator=(PointIndex&&);
void insert(const SpatElement&);
bool remove(const SpatElement&);
void insert(const PointIndexEl&);
bool remove(const PointIndexEl&);
inline void insert(const Vec3d& v, unsigned idx)
{
insert(std::make_pair(v, unsigned(idx)));
}
std::vector<SpatElement> query(std::function<bool(const SpatElement&)>);
std::vector<SpatElement> nearest(const Vec3d&, unsigned k);
std::vector<PointIndexEl> query(std::function<bool(const PointIndexEl&)>);
std::vector<PointIndexEl> nearest(const Vec3d&, unsigned k);
// For testing
size_t size() const;
bool empty() const { return size() == 0; }
void foreach(std::function<void(const SpatElement& el)> fn);
void foreach(std::function<void(const PointIndexEl& el)> fn);
};
using BoxIndexEl = std::pair<Slic3r::BoundingBox, unsigned>;
class BoxIndex {
class Impl;
// We use Pimpl because it takes a long time to compile boost headers which
// is the engine of this class. We include it only in the cpp file.
std::unique_ptr<Impl> m_impl;
public:
BoxIndex();
~BoxIndex();
BoxIndex(const BoxIndex&);
BoxIndex(BoxIndex&&);
BoxIndex& operator=(const BoxIndex&);
BoxIndex& operator=(BoxIndex&&);
void insert(const BoxIndexEl&);
inline void insert(const BoundingBox& bb, unsigned idx)
{
insert(std::make_pair(bb, unsigned(idx)));
}
bool remove(const BoxIndexEl&);
enum QueryType { qtIntersects, qtWithin };
std::vector<BoxIndexEl> query(const BoundingBox&, QueryType qt);
// For testing
size_t size() const;
bool empty() const { return size() == 0; }
void foreach(std::function<void(const BoxIndexEl& el)> fn);
};
}

View file

@ -9,10 +9,12 @@
#include "SLASpatIndex.hpp"
#include "SLABasePool.hpp"
#include <libslic3r/MTUtils.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/Model.hpp>
#include <libnest2d/optimizers/nlopt/genetic.hpp>
#include <libnest2d/optimizers/nlopt/subplex.hpp>
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
#include <libslic3r/I18N.hpp>
@ -413,7 +415,7 @@ struct Pillar {
assert(steps > 0);
height = jp(Z) - endp(Z);
if(height > 0) { // Endpoint is below the starting point
if(height > EPSILON) { // Endpoint is below the starting point
// We just create a bridge geometry with the pillar parameters and
// move the data.
@ -528,6 +530,7 @@ struct CompactBridge {
const Vec3d& ep,
const Vec3d& n,
double r,
bool endball = true,
size_t steps = 45)
{
Vec3d startp = sp + r * n;
@ -541,12 +544,14 @@ struct CompactBridge {
double fa = 2*PI/steps;
auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa);
for(auto& p : upperball.points) p += startp;
auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa);
for(auto& p : lowerball.points) p += endp;
if(endball) {
auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa);
for(auto& p : lowerball.points) p += endp;
mesh.merge(lowerball);
}
mesh.merge(upperball);
mesh.merge(lowerball);
}
};
@ -556,28 +561,111 @@ struct Pad {
PoolConfig cfg;
double zlevel = 0;
Pad() {}
Pad() = default;
Pad(const TriangleMesh& object_support_mesh,
const ExPolygons& baseplate,
Pad(const TriangleMesh& support_mesh,
const ExPolygons& modelbase,
double ground_level,
const PoolConfig& pcfg) :
cfg(pcfg),
zlevel(ground_level +
(sla::get_pad_fullheight(pcfg) - sla::get_pad_elevation(pcfg)) )
zlevel(ground_level +
sla::get_pad_fullheight(pcfg) -
sla::get_pad_elevation(pcfg))
{
ExPolygons basep;
cfg.throw_on_cancel();
Polygons basep;
auto &thr = cfg.throw_on_cancel;
thr();
// Get a sample for the pad from the support mesh
{
ExPolygons platetmp;
// The 0.1f is the layer height with which the mesh is sampled and then
// the layers are unified into one vector of polygons.
base_plate(object_support_mesh, basep,
float(cfg.min_wall_height_mm + cfg.min_wall_thickness_mm),
0.1f, pcfg.throw_on_cancel);
float zstart = float(zlevel);
float zend = zstart + float(get_pad_fullheight(pcfg) + EPSILON);
for(auto& bp : baseplate) basep.emplace_back(bp);
base_plate(support_mesh, platetmp, grid(zstart, zend, 0.1f), thr);
// We don't need no... holes control...
for (const ExPolygon &bp : platetmp)
basep.emplace_back(std::move(bp.contour));
}
if(pcfg.embed_object) {
// If the zero elevation mode is ON, we need to process the model
// base silhouette. Create the offsetted version and punch the
// breaksticks across its perimeter.
ExPolygons modelbase_offs = modelbase;
if (pcfg.embed_object.object_gap_mm > 0.0)
modelbase_offs
= offset_ex(modelbase_offs,
float(scaled(pcfg.embed_object.object_gap_mm)));
// Create a spatial index of the support silhouette polygons.
// This will be used to check for intersections with the model
// silhouette polygons. If there is no intersection, then a certain
// part of the pad is redundant as it does not host any supports.
BoxIndex bindex;
{
unsigned idx = 0;
for(auto &bp : basep) {
auto bb = bp.bounding_box();
bb.offset(float(scaled(pcfg.min_wall_thickness_mm)));
bindex.insert(bb, idx++);
}
}
// Punching the breaksticks across the offsetted polygon perimeters
ExPolygons pad_stickholes; pad_stickholes.reserve(modelbase.size());
for(auto& poly : modelbase_offs) {
std::vector<BoxIndexEl> qres =
bindex.query(poly.contour.bounding_box(),
BoxIndex::qtIntersects);
if (!qres.empty()) {
// The model silhouette polygon 'poly' HAS an intersection
// with the support silhouettes. Include this polygon
// in the pad holes with the breaksticks and merge the
// original (offsetted) version with the rest of the pad
// base plate.
basep.emplace_back(poly.contour);
// The holes of 'poly' will become positive parts of the
// pad, so they has to be checked for intersections as well
// and erased if there is no intersection with the supports
auto it = poly.holes.begin();
while(it != poly.holes.end()) {
if (bindex.query(it->bounding_box(),
BoxIndex::qtIntersects).empty())
it = poly.holes.erase(it);
else
++it;
}
// Punch the breaksticks
sla::breakstick_holes(
poly,
pcfg.embed_object.object_gap_mm, // padding
pcfg.embed_object.stick_stride_mm,
pcfg.embed_object.stick_width_mm,
pcfg.embed_object.stick_penetration_mm);
pad_stickholes.emplace_back(poly);
}
}
create_base_pool(basep, tmesh, pad_stickholes, cfg);
} else {
for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
create_base_pool(basep, tmesh, {}, cfg);
}
create_base_pool(basep, tmesh, cfg);
tmesh.translate(0, 0, float(zlevel));
}
@ -603,7 +691,7 @@ inline Vec2d to_vec2(const Vec3d& v3) {
return {v3(X), v3(Y)};
}
bool operator==(const SpatElement& e1, const SpatElement& e2) {
bool operator==(const PointIndexEl& e1, const PointIndexEl& e2) {
return e1.second == e2.second;
}
@ -620,7 +708,7 @@ ClusteredPoints cluster(const PointSet& points,
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
std::function<bool(const SpatElement&, const SpatElement&)> predicate,
std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
unsigned max_points);
// This class will hold the support tree meshes with some additional bookkeeping
@ -763,9 +851,9 @@ public:
}
const Pad& create_pad(const TriangleMesh& object_supports,
const ExPolygons& baseplate,
const ExPolygons& modelbase,
const PoolConfig& cfg) {
m_pad = Pad(object_supports, baseplate, ground_level, cfg);
m_pad = Pad(object_supports, modelbase, ground_level, cfg);
return m_pad;
}
@ -808,7 +896,6 @@ public:
merged.merge(bs.mesh);
}
if(m_ctl.stopcondition()) {
// In case of failure we have to return an empty mesh
meshcache = TriangleMesh();
@ -819,7 +906,7 @@ public:
// The mesh will be passed by const-pointer to TriangleMeshSlicer,
// which will need this.
meshcache.require_shared_vertices();
if (!meshcache.empty()) meshcache.require_shared_vertices();
// TODO: Is this necessary?
//meshcache.repair();
@ -947,7 +1034,7 @@ class SLASupportTree::Algorithm {
ThrowOnCancel m_thr;
// A spatial index to easily find strong pillars to connect to.
SpatIndex m_pillar_index;
PointIndex m_pillar_index;
inline double ray_mesh_intersect(const Vec3d& s,
const Vec3d& dir)
@ -1149,7 +1236,7 @@ class SLASupportTree::Algorithm {
auto hr = m.query_ray_hit(p + sd*dir, dir);
if(ins_check && hr.is_inside()) {
if(hr.distance() > r + sd) hits[i] = HitResult(0.0);
if(hr.distance() > 2 * r + sd) hits[i] = HitResult(0.0);
else {
// re-cast the ray from the outside of the object
auto hr2 =
@ -1264,9 +1351,12 @@ class SLASupportTree::Algorithm {
// For connecting a head to a nearby pillar.
bool connect_to_nearpillar(const Head& head, long nearpillar_id) {
auto nearpillar = [this, nearpillar_id]() { return m_result.pillar(nearpillar_id); };
if(nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false;
auto nearpillar = [this, nearpillar_id]() {
return m_result.pillar(nearpillar_id);
};
if (nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false;
Vec3d headjp = head.junction_point();
Vec3d nearjp_u = nearpillar().startpoint();
@ -1337,7 +1427,7 @@ class SLASupportTree::Algorithm {
}
bool search_pillar_and_connect(const Head& head) {
SpatIndex spindex = m_pillar_index;
PointIndex spindex = m_pillar_index;
long nearest_id = -1;
@ -1369,6 +1459,120 @@ class SLASupportTree::Algorithm {
return nearest_id >= 0;
}
// This is a proxy function for pillar creation which will mind the gap
// between the pad and the model bottom in zero elevation mode.
void create_ground_pillar(const Vec3d &jp,
const Vec3d &sourcedir,
double radius,
int head_id = -1)
{
// People were killed for this number (seriously)
static const double SQR2 = std::sqrt(2.0);
static const Vec3d DOWN = {0.0, 0.0, -1.0};
double gndlvl = m_result.ground_level;
Vec3d endp = {jp(X), jp(Y), gndlvl};
double sd = m_cfg.pillar_base_safety_distance_mm;
int pillar_id = -1;
double min_dist = sd + m_cfg.base_radius_mm + EPSILON;
double dist = 0;
bool can_add_base = true;
bool normal_mode = true;
if (m_cfg.object_elevation_mm < EPSILON
&& (dist = std::sqrt(m_mesh.squared_distance(endp))) < min_dist) {
// Get the distance from the mesh. This can be later optimized
// to get the distance in 2D plane because we are dealing with
// the ground level only.
normal_mode = false;
double mv = min_dist - dist;
double azimuth = std::atan2(sourcedir(Y), sourcedir(X));
double sinpolar = std::sin(PI - m_cfg.bridge_slope);
double cospolar = std::cos(PI - m_cfg.bridge_slope);
double cosazm = std::cos(azimuth);
double sinazm = std::sin(azimuth);
auto dir = Vec3d(cosazm * sinpolar, sinazm * sinpolar, cospolar)
.normalized();
using namespace libnest2d::opt;
StopCriteria scr;
scr.stop_score = min_dist;
SubplexOptimizer solver(scr);
auto result = solver.optimize_max(
[this, dir, jp, gndlvl](double mv) {
Vec3d endp = jp + SQR2 * mv * dir;
endp(Z) = gndlvl;
return std::sqrt(m_mesh.squared_distance(endp));
},
initvals(mv), bound(0.0, 2 * min_dist));
mv = std::get<0>(result.optimum);
endp = jp + SQR2 * mv * dir;
Vec3d pgnd = {endp(X), endp(Y), gndlvl};
can_add_base = result.score > min_dist;
double gnd_offs = m_mesh.ground_level_offset();
auto abort_in_shame =
[gnd_offs, &normal_mode, &can_add_base, &endp, jp, gndlvl]()
{
normal_mode = true;
can_add_base = false; // Nothing left to do, hope for the best
endp = {jp(X), jp(Y), gndlvl - gnd_offs };
};
// We have to check if the bridge is feasible.
if (bridge_mesh_intersect(jp, dir, radius) < (endp - jp).norm())
abort_in_shame();
else {
// If the new endpoint is below ground, do not make a pillar
if (endp(Z) < gndlvl)
endp = endp - SQR2 * (gndlvl - endp(Z)) * dir; // back off
else {
auto hit = bridge_mesh_intersect(endp, DOWN, radius);
if (!std::isinf(hit.distance())) abort_in_shame();
Pillar &plr = m_result.add_pillar(endp, pgnd, radius);
if (can_add_base)
plr.add_base(m_cfg.base_height_mm,
m_cfg.base_radius_mm);
pillar_id = plr.id;
}
m_result.add_bridge(jp, endp, radius);
m_result.add_junction(endp, radius);
// Add a degenerated pillar and the bridge.
// The degenerate pillar will have zero length and it will
// prevent from queries of head_pillar() to have non-existing
// pillar when the head should have one.
if (head_id >= 0)
m_result.add_pillar(unsigned(head_id), jp, radius);
}
}
if (normal_mode) {
Pillar &plr = head_id >= 0
? m_result.add_pillar(unsigned(head_id),
endp,
radius)
: m_result.add_pillar(jp, endp, radius);
if (can_add_base)
plr.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
pillar_id = plr.id;
}
if(pillar_id >= 0) // Save the pillar endpoint in the spatial index
m_pillar_index.insert(endp, pillar_id);
}
public:
@ -1447,9 +1651,9 @@ public:
// (Quaternion::FromTwoVectors) and apply the rotation to the
// arrow head.
double z = n(2);
double r = 1.0; // for normalized vector
double polar = std::acos(z / r);
double z = n(2);
double r = 1.0; // for normalized vector
double polar = std::acos(z / r);
double azimuth = std::atan2(n(1), n(0));
// skip if the tilt is not sane
@ -1473,14 +1677,14 @@ public:
std::cos(polar)).normalized();
// check available distance
double t = pinhead_mesh_intersect(
hp, // touching point
nn, // normal
pin_r,
m_cfg.head_back_radius_mm,
w);
EigenMesh3D::hit_result t
= pinhead_mesh_intersect(hp, // touching point
nn, // normal
pin_r,
m_cfg.head_back_radius_mm,
w);
if(t <= w) {
if(t.distance() <= w) {
// Let's try to optimize this angle, there might be a
// viable normal that doesn't collide with the model
@ -1523,12 +1727,17 @@ public:
// save the verified and corrected normal
m_support_nmls.row(fidx) = nn;
if(t > w) {
// mark the point for needing a head.
m_iheads.emplace_back(fidx);
} else if( polar >= 3*PI/4 ) {
// Headless supports do not tilt like the headed ones so
// the normal should point almost to the ground.
if (t.distance() > w) {
// Check distance from ground, we might have zero elevation.
if (hp(Z) + w * nn(Z) < m_result.ground_level) {
m_iheadless.emplace_back(fidx);
} else {
// mark the point for needing a head.
m_iheads.emplace_back(fidx);
}
} else if (polar >= 3 * PI / 4) {
// Headless supports do not tilt like the headed ones
// so the normal should point almost to the ground.
m_iheadless.emplace_back(fidx);
}
}
@ -1594,16 +1803,22 @@ public:
// from each other in the XY plane to not cross their pillar bases
// These clusters of support points will join in one pillar,
// possibly in their centroid support point.
auto pointfn = [this](unsigned i) {
return m_result.head(i).junction_point();
};
auto predicate = [this](const SpatElement& e1, const SpatElement& e2) {
auto predicate = [this](const PointIndexEl &e1,
const PointIndexEl &e2) {
double d2d = distance(to_2d(e1.first), to_2d(e2.first));
double d3d = distance(e1.first, e2.first);
return d2d < 2 * m_cfg.base_radius_mm &&
d3d < m_cfg.max_bridge_length_mm;
return d2d < 2 * m_cfg.base_radius_mm
&& d3d < m_cfg.max_bridge_length_mm;
};
m_pillar_clusters = cluster(ground_head_indices, pointfn, predicate,
m_pillar_clusters = cluster(ground_head_indices,
pointfn,
predicate,
m_cfg.max_bridges_on_pillar);
}
@ -1615,7 +1830,7 @@ public:
void routing_to_ground()
{
const double pradius = m_cfg.head_back_radius_mm;
const double gndlvl = m_result.ground_level;
// const double gndlvl = m_result.ground_level;
ClusterEl cl_centroids;
cl_centroids.reserve(m_pillar_clusters.size());
@ -1648,13 +1863,8 @@ public:
Head& h = m_result.head(hid);
h.transform();
Vec3d p = h.junction_point(); p(Z) = gndlvl;
auto& plr = m_result.add_pillar(hid, p, h.r_back_mm)
.add_base(m_cfg.base_height_mm,
m_cfg.base_radius_mm);
// Save the pillar endpoint and the pillar id in the spatial index
m_pillar_index.insert(plr.endpoint(), unsigned(plr.id));
create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id);
}
// now we will go through the clusters ones again and connect the
@ -1681,15 +1891,12 @@ public:
!search_pillar_and_connect(sidehead))
{
Vec3d pstart = sidehead.junction_point();
Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl};
//Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl};
// Could not find a pillar, create one
auto& pillar = m_result.add_pillar(unsigned(sidehead.id),
pend, pradius)
.add_base(m_cfg.base_height_mm,
m_cfg.base_radius_mm);
// connects to ground, eligible for bridging
m_pillar_index.insert(pend, unsigned(pillar.id));
create_ground_pillar(pstart,
sidehead.dir,
pradius,
sidehead.id);
}
}
}
@ -1718,12 +1925,7 @@ public:
m_result.add_bridge(hjp, endp, head.r_back_mm);
m_result.add_junction(endp, head.r_back_mm);
auto groundp = endp;
groundp(Z) = m_result.ground_level;
auto& newpillar = m_result.add_pillar(endp, groundp, head.r_back_mm)
.add_base(m_cfg.base_height_mm,
m_cfg.base_radius_mm);
m_pillar_index.insert(groundp, unsigned(newpillar.id));
this->create_ground_pillar(endp, dir, head.r_back_mm);
};
std::vector<unsigned> modelpillars;
@ -1883,6 +2085,28 @@ public:
m_pillar_index.insert(pillar.endpoint(), pillid);
}
}
// Helper function for interconnect_pillars where pairs of already connected
// pillars should be checked for not to be processed again. This can be done
// in O(log) or even constant time with a set or an unordered set of hash
// values uniquely representing a pair of integers. The order of numbers
// within the pair should not matter, it has the same unique hash.
template<class I> static I pairhash(I a, I b)
{
using std::ceil; using std::log2; using std::max; using std::min;
static_assert(std::is_integral<I>::value,
"This function works only for integral types.");
I g = min(a, b), l = max(a, b);
auto bits_g = g ? int(ceil(log2(g))) : 0;
// Assume the hash will fit into the output variable
assert((l ? (ceil(log2(l))) : 0) + bits_g < int(sizeof(I) * CHAR_BIT));
return (l << bits_g) + g;
}
void interconnect_pillars() {
// Now comes the algorithm that connects pillars with each other.
@ -1900,45 +2124,51 @@ public:
double min_height_ratio = 0.5;
std::set<unsigned long> pairs;
// A function to connect one pillar with its neighbors. THe number of
// neighbors is given in the configuration. This function if called
// for every pillar in the pillar index. A pair of pillar will not
// be connected multiple times this is ensured by the 'pairs' set which
// remembers the processed pillar pairs
auto cascadefn =
[this, d, &pairs, min_height_ratio, H1] (const SpatElement& el)
[this, d, &pairs, min_height_ratio, H1] (const PointIndexEl& el)
{
Vec3d qp = el.first;
const Pillar& pillar = m_result.pillar(el.second);
Vec3d qp = el.first; // endpoint of the pillar
const Pillar& pillar = m_result.pillar(el.second); // actual pillar
// Get the max number of neighbors a pillar should connect to
unsigned neighbors = m_cfg.pillar_cascade_neighbors;
// connections are enough for one pillar
// connections are already enough for the pillar
if(pillar.links >= neighbors) return;
// Query all remaining points within reach
auto qres = m_pillar_index.query([qp, d](const SpatElement& e){
auto qres = m_pillar_index.query([qp, d](const PointIndexEl& e){
return distance(e.first, qp) < d;
});
// sort the result by distance (have to check if this is needed)
std::sort(qres.begin(), qres.end(),
[qp](const SpatElement& e1, const SpatElement& e2){
[qp](const PointIndexEl& e1, const PointIndexEl& e2){
return distance(e1.first, qp) < distance(e2.first, qp);
});
for(auto& re : qres) {
for(auto& re : qres) { // process the queried neighbors
if(re.second == el.second) continue;
if(re.second == el.second) continue; // Skip self
auto a = el.second, b = re.second;
// I hope that the area of a square is never equal to its
// circumference
auto hashval = 2 * (a + b) + a * b;
// Get unique hash for the given pair (order doesn't matter)
auto hashval = pairhash(a, b);
// Search for the pair amongst the remembered pairs
if(pairs.find(hashval) != pairs.end()) continue;
const Pillar& neighborpillar = m_result.pillars()[re.second];
// this neighbor is occupied
// this neighbor is occupied, skip
if(neighborpillar.links >= neighbors) continue;
if(interconnect(pillar, neighborpillar)) {
@ -1960,47 +2190,79 @@ public:
if(pillar.links >= neighbors) break;
}
};
// Run the cascade for the pillars in the index
m_pillar_index.foreach(cascadefn);
// We would be done here if we could allow some pillars to not be
// connected with any neighbors. But this might leave the support tree
// unprintable.
//
// The current solution is to insert additional pillars next to these
// lonely pillars. One or even two additional pillar might get inserted
// depending on the length of the lonely pillar.
size_t pillarcount = m_result.pillars().size();
// Again, go through all pillars, this time in the whole support tree
// not just the index.
for(size_t pid = 0; pid < pillarcount; pid++) {
auto pillar = [this, pid]() { return m_result.pillar(pid); };
// Decide how many additional pillars will be needed:
unsigned needpillars = 0;
if(pillar().bridges > m_cfg.max_bridges_on_pillar) needpillars = 3;
else if(pillar().links < 2 && pillar().height > H2) {
if (pillar().bridges > m_cfg.max_bridges_on_pillar)
needpillars = 3;
else if (pillar().links < 2 && pillar().height > H2) {
// Not enough neighbors to support this pillar
needpillars = 2 - pillar().links;
}
else if(pillar().links < 1 && pillar().height > H1) {
} else if (pillar().links < 1 && pillar().height > H1) {
// No neighbors could be found and the pillar is too long.
needpillars = 1;
}
// Search for new pillar locations
bool found = false;
double alpha = 0; // goes to 2Pi
double r = 2 * m_cfg.base_radius_mm;
Vec3d pillarsp = pillar().startpoint();
// Search for new pillar locations:
bool found = false;
double alpha = 0; // goes to 2Pi
double r = 2 * m_cfg.base_radius_mm;
Vec3d pillarsp = pillar().startpoint();
// temp value for starting point detection
Vec3d sp(pillarsp(X), pillarsp(Y), pillarsp(Z) - r);
std::vector<bool> tv(needpillars, false);
std::vector<Vec3d> spts(needpillars);
// A vector of bool for placement feasbility
std::vector<bool> canplace(needpillars, false);
std::vector<Vec3d> spts(needpillars); // vector of starting points
double gnd = m_result.ground_level;
double min_dist = m_cfg.pillar_base_safety_distance_mm +
m_cfg.base_radius_mm + EPSILON;
while(!found && alpha < 2*PI) {
for(unsigned n = 0; n < needpillars; n++) {
double a = alpha + n * PI/3;
Vec3d s = sp;
for (unsigned n = 0;
n < needpillars && (!n || canplace[n - 1]);
n++)
{
double a = alpha + n * PI / 3;
Vec3d s = sp;
s(X) += std::cos(a) * r;
s(Y) += std::sin(a) * r;
spts[n] = s;
// Check the path vertically down
auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r);
tv[n] = std::isinf(hr.distance());
Vec3d gndsp{s(X), s(Y), gnd};
// If the path is clear, check for pillar base collisions
canplace[n] = std::isinf(hr.distance()) &&
std::sqrt(m_mesh.squared_distance(gndsp)) >
min_dist;
}
found = std::all_of(tv.begin(), tv.end(), [](bool v){return v;});
found = std::all_of(canplace.begin(), canplace.end(),
[](bool v) { return v; });
// 20 angles will be tried...
alpha += 0.1 * PI;
@ -2010,7 +2272,7 @@ public:
newpills.reserve(needpillars);
if(found) for(unsigned n = 0; n < needpillars; n++) {
Vec3d s = spts[n]; double gnd = m_result.ground_level;
Vec3d s = spts[n];
Pillar p(s, Vec3d(s(X), s(Y), gnd), pillar().r);
p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
@ -2075,9 +2337,13 @@ public:
// This is only for checking
double idist = bridge_mesh_intersect(sph, dir, R, true);
double dist = ray_mesh_intersect(sj, dir);
if (std::isinf(dist))
dist = sph(Z) - m_mesh.ground_level()
+ m_mesh.ground_level_offset();
if(std::isinf(idist) || std::isnan(idist) || idist < 2*R ||
std::isinf(dist) || std::isnan(dist) || dist < 2*R) {
if(std::isnan(idist) || idist < 2*R ||
std::isnan(dist) || dist < 2*R)
{
BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless"
<< " support stick at: "
<< sj.transpose();
@ -2085,7 +2351,7 @@ public:
}
Vec3d ej = sj + (dist + HWIDTH_MM)* dir;
m_result.add_compact_bridge(sp, ej, n, R);
m_result.add_compact_bridge(sp, ej, n, R, !std::isinf(dist));
}
}
};
@ -2214,7 +2480,9 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
return pc == ABORT;
}
SLASupportTree::SLASupportTree(): m_impl(new Impl()) {}
SLASupportTree::SLASupportTree(double gnd_lvl): m_impl(new Impl()) {
m_impl->ground_level = gnd_lvl;
}
const TriangleMesh &SLASupportTree::merged_mesh() const
{
@ -2226,7 +2494,7 @@ void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const {
outmesh.merge(get_pad());
}
SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
std::vector<ExPolygons> SLASupportTree::slice(float layerh, float init_layerh) const
{
if(init_layerh < 0) init_layerh = layerh;
auto& stree = get();
@ -2245,36 +2513,31 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad());
fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
if (!fullmesh.empty()) fullmesh.require_shared_vertices();
TriangleMeshSlicer slicer(&fullmesh);
SlicedSupports ret;
std::vector<ExPolygons> ret;
slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
return ret;
}
SlicedSupports SLASupportTree::slice(const std::vector<float> &heights,
std::vector<ExPolygons> SLASupportTree::slice(const std::vector<float> &heights,
float cr) const
{
TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad());
fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
if (!fullmesh.empty()) fullmesh.require_shared_vertices();
TriangleMeshSlicer slicer(&fullmesh);
SlicedSupports ret;
std::vector<ExPolygons> ret;
slicer.slice(heights, cr, &ret, get().ctl().cancelfn);
return ret;
}
const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
const TriangleMesh &SLASupportTree::add_pad(const ExPolygons& modelbase,
const PoolConfig& pcfg) const
{
// PoolConfig pcfg;
// pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
// pcfg.min_wall_height_mm = min_wall_height_mm;
// pcfg.max_merge_distance_mm = max_merge_distance_mm;
// pcfg.edge_radius_mm = edge_radius_mm;
return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh;
return m_impl->create_pad(merged_mesh(), modelbase, pcfg).tmesh;
}
const TriangleMesh &SLASupportTree::get_pad() const

View file

@ -24,10 +24,11 @@ class TriangleMesh;
class Model;
class ModelInstance;
class ModelObject;
class Polygon;
class ExPolygon;
using SliceLayer = std::vector<ExPolygon>;
using SlicedSupports = std::vector<SliceLayer>;
using Polygons = std::vector<Polygon>;
using ExPolygons = std::vector<ExPolygon>;
namespace sla {
@ -80,6 +81,10 @@ struct SupportConfig {
// 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 shortest distance between a pillar base perimeter from the model
// body. This is only useful when elevation is set to zero.
double pillar_base_safety_distance_mm = 0.5;
// /////////////////////////////////////////////////////////////////////////
// Compile time configuration values (candidates for runtime)
@ -160,7 +165,7 @@ class SLASupportTree {
public:
SLASupportTree();
SLASupportTree(double ground_level = 0.0);
SLASupportTree(const std::vector<SupportPoint>& pts,
const EigenMesh3D& em,
@ -179,12 +184,17 @@ public:
void merged_mesh_with_pad(TriangleMesh&) const;
/// Get the sliced 2d layers of the support geometry.
SlicedSupports slice(float layerh, float init_layerh = -1.0) const;
std::vector<ExPolygons> slice(float layerh, float init_layerh = -1.0) const;
SlicedSupports slice(const std::vector<float>&, float closing_radius) const;
std::vector<ExPolygons> slice(const std::vector<float> &,
float closing_radius) const;
/// Adding the "pad" (base pool) under the supports
const TriangleMesh& add_pad(const SliceLayer& baseplate,
/// modelbase will be used according to the embed_object flag in PoolConfig.
/// If set, the plate will interpreted as the model's intrinsic pad.
/// Otherwise, the modelbase will be unified with the base plate calculated
/// from the supports.
const TriangleMesh& add_pad(const ExPolygons& modelbase,
const PoolConfig& pcfg) const;
/// Get the pad geometry

View file

@ -29,69 +29,137 @@ namespace sla {
using igl::PI;
/* **************************************************************************
* SpatIndex implementation
* PointIndex implementation
* ************************************************************************** */
class SpatIndex::Impl {
class PointIndex::Impl {
public:
using BoostIndex = boost::geometry::index::rtree< SpatElement,
using BoostIndex = boost::geometry::index::rtree< PointIndexEl,
boost::geometry::index::rstar<16, 4> /* ? */ >;
BoostIndex m_store;
};
SpatIndex::SpatIndex(): m_impl(new Impl()) {}
SpatIndex::~SpatIndex() {}
PointIndex::PointIndex(): m_impl(new Impl()) {}
PointIndex::~PointIndex() {}
SpatIndex::SpatIndex(const SpatIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
SpatIndex::SpatIndex(SpatIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
PointIndex::PointIndex(const PointIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
PointIndex::PointIndex(PointIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
SpatIndex& SpatIndex::operator=(const SpatIndex &cpy)
PointIndex& PointIndex::operator=(const PointIndex &cpy)
{
m_impl.reset(new Impl(*cpy.m_impl));
return *this;
}
SpatIndex& SpatIndex::operator=(SpatIndex &&cpy)
PointIndex& PointIndex::operator=(PointIndex &&cpy)
{
m_impl.swap(cpy.m_impl);
return *this;
}
void SpatIndex::insert(const SpatElement &el)
void PointIndex::insert(const PointIndexEl &el)
{
m_impl->m_store.insert(el);
}
bool SpatIndex::remove(const SpatElement& el)
bool PointIndex::remove(const PointIndexEl& el)
{
return m_impl->m_store.remove(el) == 1;
}
std::vector<SpatElement>
SpatIndex::query(std::function<bool(const SpatElement &)> fn)
std::vector<PointIndexEl>
PointIndex::query(std::function<bool(const PointIndexEl &)> fn)
{
namespace bgi = boost::geometry::index;
std::vector<SpatElement> ret;
std::vector<PointIndexEl> ret;
m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret));
return ret;
}
std::vector<SpatElement> SpatIndex::nearest(const Vec3d &el, unsigned k = 1)
std::vector<PointIndexEl> PointIndex::nearest(const Vec3d &el, unsigned k = 1)
{
namespace bgi = boost::geometry::index;
std::vector<SpatElement> ret; ret.reserve(k);
std::vector<PointIndexEl> ret; ret.reserve(k);
m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret));
return ret;
}
size_t SpatIndex::size() const
size_t PointIndex::size() const
{
return m_impl->m_store.size();
}
void SpatIndex::foreach(std::function<void (const SpatElement &)> fn)
void PointIndex::foreach(std::function<void (const PointIndexEl &)> fn)
{
for(auto& el : m_impl->m_store) fn(el);
}
/* **************************************************************************
* BoxIndex implementation
* ************************************************************************** */
class BoxIndex::Impl {
public:
using BoostIndex = boost::geometry::index::
rtree<BoxIndexEl, boost::geometry::index::rstar<16, 4> /* ? */>;
BoostIndex m_store;
};
BoxIndex::BoxIndex(): m_impl(new Impl()) {}
BoxIndex::~BoxIndex() {}
BoxIndex::BoxIndex(const BoxIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
BoxIndex::BoxIndex(BoxIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
BoxIndex& BoxIndex::operator=(const BoxIndex &cpy)
{
m_impl.reset(new Impl(*cpy.m_impl));
return *this;
}
BoxIndex& BoxIndex::operator=(BoxIndex &&cpy)
{
m_impl.swap(cpy.m_impl);
return *this;
}
void BoxIndex::insert(const BoxIndexEl &el)
{
m_impl->m_store.insert(el);
}
bool BoxIndex::remove(const BoxIndexEl& el)
{
return m_impl->m_store.remove(el) == 1;
}
std::vector<BoxIndexEl> BoxIndex::query(const BoundingBox &qrbb,
BoxIndex::QueryType qt)
{
namespace bgi = boost::geometry::index;
std::vector<BoxIndexEl> ret; ret.reserve(m_impl->m_store.size());
switch (qt) {
case qtIntersects:
m_impl->m_store.query(bgi::intersects(qrbb), std::back_inserter(ret));
break;
case qtWithin:
m_impl->m_store.query(bgi::within(qrbb), std::back_inserter(ret));
}
return ret;
}
size_t BoxIndex::size() const
{
return m_impl->m_store.size();
}
void BoxIndex::foreach(std::function<void (const BoxIndexEl &)> fn)
{
for(auto& el : m_impl->m_store) fn(el);
}
@ -343,12 +411,14 @@ PointSet normals(const PointSet& points,
return ret;
}
namespace bgi = boost::geometry::index;
using Index3D = bgi::rtree< SpatElement, bgi::rstar<16, 4> /* ? */ >;
using Index3D = bgi::rtree< PointIndexEl, bgi::rstar<16, 4> /* ? */ >;
ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
std::function<std::vector<SpatElement>(const Index3D&, const SpatElement&)> qfn)
ClusteredPoints cluster(Index3D &sindex,
unsigned max_points,
std::function<std::vector<PointIndexEl>(
const Index3D &, const PointIndexEl &)> qfn)
{
using Elems = std::vector<SpatElement>;
using Elems = std::vector<PointIndexEl>;
// Recursive function for visiting all the points in a given distance to
// each other
@ -356,8 +426,8 @@ ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
[&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster)
{
for(auto& p : pts) {
std::vector<SpatElement> tmp = qfn(sindex, p);
auto cmp = [](const SpatElement& e1, const SpatElement& e2){
std::vector<PointIndexEl> tmp = qfn(sindex, p);
auto cmp = [](const PointIndexEl& e1, const PointIndexEl& e2){
return e1.second < e2.second;
};
@ -401,12 +471,12 @@ ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
}
namespace {
std::vector<SpatElement> distance_queryfn(const Index3D& sindex,
const SpatElement& p,
std::vector<PointIndexEl> distance_queryfn(const Index3D& sindex,
const PointIndexEl& p,
double dist,
unsigned max_points)
{
std::vector<SpatElement> tmp; tmp.reserve(max_points);
std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
sindex.query(
bgi::nearest(p.first, max_points),
std::back_inserter(tmp)
@ -433,7 +503,7 @@ ClusteredPoints cluster(
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)
[dist, max_points](const Index3D& sidx, const PointIndexEl& p)
{
return distance_queryfn(sidx, p, dist, max_points);
});
@ -443,7 +513,7 @@ ClusteredPoints cluster(
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
std::function<bool(const SpatElement&, const SpatElement&)> predicate,
std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
unsigned max_points)
{
// A spatial index for querying the nearest points
@ -453,10 +523,10 @@ ClusteredPoints cluster(
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)
[max_points, predicate](const Index3D& sidx, const PointIndexEl& p)
{
std::vector<SpatElement> tmp; tmp.reserve(max_points);
sidx.query(bgi::satisfies([p, predicate](const SpatElement& e){
std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
sidx.query(bgi::satisfies([p, predicate](const PointIndexEl& e){
return predicate(p, e);
}), std::back_inserter(tmp));
return tmp;
@ -473,7 +543,7 @@ ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points)
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)
[dist, max_points](const Index3D& sidx, const PointIndexEl& p)
{
return distance_queryfn(sidx, p, dist, max_points);
});

View file

@ -31,11 +31,10 @@ using SupportTreePtr = std::unique_ptr<sla::SLASupportTree>;
class SLAPrintObject::SupportData
{
public:
sla::EigenMesh3D emesh; // index-triangle representation
std::vector<sla::SupportPoint>
support_points; // all the support points (manual/auto)
SupportTreePtr support_tree_ptr; // the supports
SlicedSupports support_slices; // sliced supports
sla::EigenMesh3D emesh; // index-triangle representation
std::vector<sla::SupportPoint> support_points; // all the support points (manual/auto)
SupportTreePtr support_tree_ptr; // the supports
std::vector<ExPolygons> support_slices; // sliced supports
inline SupportData(const TriangleMesh &trmesh) : emesh(trmesh) {}
};
@ -441,12 +440,10 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
update_apply_status(this->invalidate_all_steps());
m_objects = print_objects_new;
// Delete the PrintObjects marked as Unknown or Deleted.
bool deleted_objects = false;
for (auto &pos : print_object_status)
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
update_apply_status(pos.print_object->invalidate_all_steps());
delete pos.print_object;
deleted_objects = true;
}
if (new_objects)
update_apply_status(false);
@ -473,7 +470,7 @@ void SLAPrint::set_task(const TaskParams &params)
int n_object_steps = int(params.to_object_step) + 1;
if (n_object_steps == 0)
n_object_steps = (int)slaposCount;
n_object_steps = int(slaposCount);
if (params.single_model_object.valid()) {
// Find the print object to be processed with priority.
@ -488,7 +485,7 @@ void SLAPrint::set_task(const TaskParams &params)
// Find out whether the priority print object is being currently processed.
bool running = false;
for (int istep = 0; istep < n_object_steps; ++ istep) {
if (! print_object->m_stepmask[istep])
if (! print_object->m_stepmask[size_t(istep)])
// Step was skipped, cancel.
break;
if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
@ -504,7 +501,7 @@ void SLAPrint::set_task(const TaskParams &params)
if (params.single_model_instance_only) {
// Suppress all the steps of other instances.
for (SLAPrintObject *po : m_objects)
for (int istep = 0; istep < (int)slaposCount; ++ istep)
for (size_t istep = 0; istep < slaposCount; ++ istep)
po->m_stepmask[istep] = false;
} else if (! running) {
// Swap the print objects, so that the selected print_object is first in the row.
@ -514,15 +511,15 @@ void SLAPrint::set_task(const TaskParams &params)
}
// and set the steps for the current object.
for (int istep = 0; istep < n_object_steps; ++ istep)
print_object->m_stepmask[istep] = true;
for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
print_object->m_stepmask[istep] = false;
print_object->m_stepmask[size_t(istep)] = true;
for (int istep = n_object_steps; istep < int(slaposCount); ++ istep)
print_object->m_stepmask[size_t(istep)] = false;
} else {
// Slicing all objects.
bool running = false;
for (SLAPrintObject *print_object : m_objects)
for (int istep = 0; istep < n_object_steps; ++ istep) {
if (! print_object->m_stepmask[istep]) {
if (! print_object->m_stepmask[size_t(istep)]) {
// Step may have been skipped. Restart.
goto loop_end;
}
@ -538,8 +535,8 @@ void SLAPrint::set_task(const TaskParams &params)
this->call_cancel_callback();
for (SLAPrintObject *po : m_objects) {
for (int istep = 0; istep < n_object_steps; ++ istep)
po->m_stepmask[istep] = true;
for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
po->m_stepmask[size_t(istep)] = true;
for (auto istep = size_t(n_object_steps); istep < slaposCount; ++ istep)
po->m_stepmask[istep] = false;
}
}
@ -557,9 +554,9 @@ void SLAPrint::set_task(const TaskParams &params)
void SLAPrint::finalize()
{
for (SLAPrintObject *po : m_objects)
for (int istep = 0; istep < (int)slaposCount; ++ istep)
for (size_t istep = 0; istep < slaposCount; ++ istep)
po->m_stepmask[istep] = true;
for (int istep = 0; istep < (int)slapsCount; ++ istep)
for (size_t istep = 0; istep < slapsCount; ++ istep)
m_stepmask[istep] = true;
}
@ -597,21 +594,48 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat();
scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat();
scfg.base_height_mm = c.support_base_height.getFloat();
scfg.pillar_base_safety_distance_mm =
c.support_base_safety_distance.getFloat() < EPSILON ?
scfg.safety_distance_mm : c.support_base_safety_distance.getFloat();
return scfg;
}
sla::PoolConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) {
sla::PoolConfig::EmbedObject ret;
ret.enabled = c.support_object_elevation.getFloat() <= EPSILON &&
c.pad_enable.getBool() && c.supports_enable.getBool();
if(ret.enabled) {
ret.object_gap_mm = c.pad_object_gap.getFloat();
ret.stick_width_mm = c.pad_object_connector_width.getFloat();
ret.stick_stride_mm = c.pad_object_connector_stride.getFloat();
ret.stick_penetration_mm = c.pad_object_connector_penetration
.getFloat();
}
return ret;
}
sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) {
sla::PoolConfig pcfg;
pcfg.min_wall_thickness_mm = c.pad_wall_thickness.getFloat();
pcfg.wall_slope = c.pad_wall_slope.getFloat();
pcfg.edge_radius_mm = c.pad_edge_radius.getFloat();
pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0;
// We do not support radius for now
pcfg.edge_radius_mm = 0.0; //c.pad_edge_radius.getFloat();
pcfg.max_merge_distance_mm = c.pad_max_merge_distance.getFloat();
pcfg.min_wall_height_mm = c.pad_wall_height.getFloat();
// set builtin pad implicitly ON
pcfg.embed_object = builtin_pad_cfg(c);
return pcfg;
}
}
std::string SLAPrint::validate() const
@ -634,9 +658,21 @@ std::string SLAPrint::validate() const
cfg.head_width_mm +
2 * cfg.head_back_radius_mm -
cfg.head_penetration_mm;
double elv = cfg.object_elevation_mm;
if(supports_en && pinhead_width > cfg.object_elevation_mm)
if(supports_en && elv > EPSILON && elv < pinhead_width )
return L("Elevation is too low for object.");
sla::PoolConfig::EmbedObject builtinpad = builtin_pad_cfg(po->config());
if(supports_en && builtinpad.enabled &&
cfg.pillar_base_safety_distance_mm < builtinpad.object_gap_mm) {
return L(
"The endings of the support pillars will be deployed on the "
"gap between the object and the pad. 'Support base safety "
"distance' has to be greater than the 'Pad object gap' "
"parameter to avoid this.");
}
}
return "";
@ -751,18 +787,27 @@ void SLAPrint::process()
mit->set_model_slice_idx(po, id); ++mit;
}
if(po.m_config.supports_enable.getBool() ||
po.m_config.pad_enable.getBool())
{
po.m_supportdata.reset(
new SLAPrintObject::SupportData(po.transformed_mesh()) );
}
};
// In this step we check the slices, identify island and cover them with
// support points. Then we sprinkle the rest of the mesh.
auto support_points = [this, ostepd](SLAPrintObject& po) {
const ModelObject& mo = *po.m_model_object;
po.m_supportdata.reset(
new SLAPrintObject::SupportData(po.transformed_mesh()) );
// If supports are disabled, we can skip the model scan.
if(!po.m_config.supports_enable.getBool()) return;
if (!po.m_supportdata)
po.m_supportdata.reset(
new SLAPrintObject::SupportData(po.transformed_mesh()));
const ModelObject& mo = *po.m_model_object;
BOOST_LOG_TRIVIAL(debug) << "Support point count "
<< mo.sla_support_points.size();
@ -771,7 +816,7 @@ void SLAPrint::process()
// into the backend cache.
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
// Hypotetical use of the slice index:
// Hypothetical use of the slice index:
// auto bb = po.transformed_mesh().bounding_box();
// auto range = po.get_slice_records(bb.min(Z));
// std::vector<float> heights; heights.reserve(range.size());
@ -820,23 +865,59 @@ void SLAPrint::process()
BOOST_LOG_TRIVIAL(debug) << "Automatic support points: "
<< po.m_supportdata->support_points.size();
// Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass the update status to GLGizmoSlaSupports
m_report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
// Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass
// the update status to GLGizmoSlaSupports
m_report_status(*this,
-1,
L("Generating support points"),
SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
}
else {
// There are either some points on the front-end, or the user removed them on purpose. No calculation will be done.
// There are either some points on the front-end, or the user
// removed them on purpose. No calculation will be done.
po.m_supportdata->support_points = po.transformed_support_points();
}
// If the zero elevation mode is engaged, we have to filter out all the
// points that are on the bottom of the object
if (po.config().support_object_elevation.getFloat() <= EPSILON) {
double gnd = po.m_supportdata->emesh.ground_level();
auto & pts = po.m_supportdata->support_points;
double tolerance = po.config().pad_enable.getBool()
? po.m_config.pad_wall_thickness.getFloat()
: po.m_config.support_base_height.getFloat();
// get iterator to the reorganized vector end
auto endit = std::remove_if(
pts.begin(),
pts.end(),
[tolerance, gnd](const sla::SupportPoint &sp) {
double diff = std::abs(gnd - double(sp.pos(Z)));
return diff <= tolerance;
});
// erase all elements after the new end
pts.erase(endit, pts.end());
}
};
// In this step we create the supports
auto support_tree = [this, ostepd](SLAPrintObject& po)
{
if(!po.m_supportdata) return;
sla::PoolConfig pcfg = make_pool_config(po.m_config);
if (pcfg.embed_object)
po.m_supportdata->emesh.ground_level_offset(
pcfg.min_wall_thickness_mm);
if(!po.m_config.supports_enable.getBool()) {
// Generate empty support tree. It can still host a pad
po.m_supportdata->support_tree_ptr.reset(new SLASupportTree());
po.m_supportdata->support_tree_ptr.reset(
new SLASupportTree(po.m_supportdata->emesh.ground_level()));
return;
}
@ -858,7 +939,7 @@ void SLAPrint::process()
ctl.stopcondition = [this](){ return canceled(); };
ctl.cancelfn = [this]() { throw_if_canceled(); };
po.m_supportdata->support_tree_ptr.reset(
new SLASupportTree(po.m_supportdata->support_points,
po.m_supportdata->emesh, scfg, ctl));
@ -888,40 +969,33 @@ void SLAPrint::process()
// and before the supports had been sliced. (or the slicing has to be
// repeated)
if(!po.m_supportdata || !po.m_supportdata->support_tree_ptr) {
BOOST_LOG_TRIVIAL(error) << "Uninitialized support data at "
<< "pad creation.";
return;
}
if(po.m_config.pad_enable.getBool())
{
double wt = po.m_config.pad_wall_thickness.getFloat();
double h = po.m_config.pad_wall_height.getFloat();
double md = po.m_config.pad_max_merge_distance.getFloat();
// Radius is disabled for now...
double er = 0; // po.m_config.pad_edge_radius.getFloat();
double tilt = po.m_config.pad_wall_slope.getFloat() * PI / 180.0;
double lh = po.m_config.layer_height.getFloat();
double elevation = po.m_config.support_object_elevation.getFloat();
if(!po.m_config.supports_enable.getBool()) elevation = 0;
sla::PoolConfig pcfg(wt, h, md, er, tilt);
// Get the distilled pad configuration from the config
sla::PoolConfig pcfg = make_pool_config(po.m_config);
ExPolygons bp;
double pad_h = sla::get_pad_fullheight(pcfg);
auto&& trmesh = po.transformed_mesh();
ExPolygons bp; // This will store the base plate of the pad.
double pad_h = sla::get_pad_fullheight(pcfg);
const TriangleMesh &trmesh = po.transformed_mesh();
// This call can get pretty time consuming
auto thrfn = [this](){ throw_if_canceled(); };
if(elevation < pad_h) {
// we have to count with the model geometry for the base plate
sla::base_plate(trmesh, bp, float(pad_h), float(lh), thrfn);
if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) {
// No support (thus no elevation) or zero elevation mode
// we sometimes call it "builtin pad" is enabled so we will
// get a sample from the bottom of the mesh and use it for pad
// creation.
sla::base_plate(trmesh,
bp,
float(pad_h),
float(po.m_config.layer_height.getFloat()),
thrfn);
}
pcfg.throw_on_cancel = thrfn;
po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg);
} else {
} else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) {
po.m_supportdata->support_tree_ptr->remove_pad();
}
@ -938,6 +1012,11 @@ void SLAPrint::process()
if(sd) sd->support_slices.clear();
// Don't bother if no supports and no pad is present.
if (!po.m_config.supports_enable.getBool() &&
!po.m_config.pad_enable.getBool())
return;
if(sd && sd->support_tree_ptr) {
std::vector<float> heights; heights.reserve(po.m_slice_index.size());
@ -964,7 +1043,8 @@ void SLAPrint::process()
po.m_slice_index[i].set_support_slice_idx(po, i);
}
// Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices.
// Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update
// status to the 3D preview to load the SLA slices.
m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
};
@ -1536,14 +1616,17 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
return true;
}
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
Inherited(print, model_object),
m_stepmask(slaposCount, true),
m_transformed_rmesh( [this](TriangleMesh& obj){
obj = m_model_object->raw_mesh(); obj.transform(m_trafo); obj.require_shared_vertices();
})
{
}
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object)
: Inherited(print, model_object)
, m_stepmask(slaposCount, true)
, m_transformed_rmesh([this](TriangleMesh &obj) {
obj = m_model_object->raw_mesh();
if (!obj.empty()) {
obj.transform(m_trafo);
obj.require_shared_vertices();
}
})
{}
SLAPrintObject::~SLAPrintObject() {}
@ -1582,13 +1665,19 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
|| opt_key == "support_critical_angle"
|| opt_key == "support_max_bridge_length"
|| opt_key == "support_max_pillar_link_distance"
|| opt_key == "support_base_safety_distance"
) {
steps.emplace_back(slaposSupportTree);
} else if (
opt_key == "pad_wall_height"
|| opt_key == "pad_max_merge_distance"
|| opt_key == "pad_wall_slope"
|| opt_key == "pad_edge_radius") {
|| opt_key == "pad_edge_radius"
|| opt_key == "pad_object_gap"
|| opt_key == "pad_object_connector_stride"
|| opt_key == "pad_object_connector_width"
|| opt_key == "pad_object_connector_penetration"
) {
steps.emplace_back(slaposBasePool);
} else {
// All keys should be covered.
@ -1629,17 +1718,16 @@ bool SLAPrintObject::invalidate_all_steps()
}
double SLAPrintObject::get_elevation() const {
bool se = m_config.supports_enable.getBool();
double ret = se? m_config.support_object_elevation.getFloat() : 0;
bool en = m_config.supports_enable.getBool();
double ret = en ? m_config.support_object_elevation.getFloat() : 0.;
// if the pad is enabled, then half of the pad height is its base plate
if(m_config.pad_enable.getBool()) {
// Normally the elevation for the pad itself would be the thickness of
// its walls but currently it is half of its thickness. Whatever it
// will be in the future, we provide the config to the get_pad_elevation
// method and we will have the correct value
sla::PoolConfig pcfg = make_pool_config(m_config);
ret += sla::get_pad_elevation(pcfg);
if(!pcfg.embed_object) ret += sla::get_pad_elevation(pcfg);
}
return ret;
@ -1647,14 +1735,14 @@ double SLAPrintObject::get_elevation() const {
double SLAPrintObject::get_current_elevation() const
{
bool se = m_config.supports_enable.getBool();
bool has_supports = is_step_done(slaposSupportTree);
bool has_pad = is_step_done(slaposBasePool);
if(!has_supports && !has_pad)
return 0;
else if(has_supports && !has_pad)
return se ? m_config.support_object_elevation.getFloat() : 0;
else if(has_supports && !has_pad) {
return m_config.support_object_elevation.getFloat();
}
return get_elevation();
}
@ -1682,13 +1770,14 @@ namespace { // dummy empty static containers for return values in some methods
const std::vector<ExPolygons> EMPTY_SLICES;
const TriangleMesh EMPTY_MESH;
const ExPolygons EMPTY_SLICE;
const std::vector<sla::SupportPoint> EMPTY_SUPPORT_POINTS;
}
const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f);
const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const
{
return m_supportdata->support_points;
return m_supportdata? m_supportdata->support_points : EMPTY_SUPPORT_POINTS;
}
const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const

View file

@ -41,13 +41,4 @@
#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7)
//====================
// 1.42.0.alpha8 techs
//====================
#define ENABLE_1_42_0_ALPHA8 1
// Toolbars and Gizmos use icons imported from svg files
#define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG)
#endif // _technologies_h_

View file

@ -410,6 +410,8 @@ public:
BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const;
// caching variant
const BoundingBoxf3& transformed_convex_hull_bounding_box() const;
// convex hull
const TriangleMesh* convex_hull() const { return m_convex_hull.get(); }
bool empty() const { return this->indexed_vertex_array.empty(); }

View file

@ -229,6 +229,33 @@ std::string AppConfig::get_last_dir() const
return std::string();
}
std::vector<std::string> AppConfig::get_recent_projects() const
{
std::vector<std::string> ret;
const auto it = m_storage.find("recent_projects");
if (it != m_storage.end())
{
for (const std::map<std::string, std::string>::value_type& item : it->second)
{
ret.push_back(item.second);
}
}
return ret;
}
void AppConfig::set_recent_projects(const std::vector<std::string>& recent_projects)
{
auto it = m_storage.find("recent_projects");
if (it == m_storage.end())
it = m_storage.insert(std::map<std::string, std::map<std::string, std::string>>::value_type("recent_projects", std::map<std::string, std::string>())).first;
it->second.clear();
for (unsigned int i = 0; i < (unsigned int)recent_projects.size(); ++i)
{
it->second[std::to_string(i + 1)] = recent_projects[i];
}
}
void AppConfig::update_config_dir(const std::string &dir)
{
this->set("recent", "config_directory", dir);

View file

@ -122,6 +122,9 @@ public:
// Does the config file exist?
static bool exists();
std::vector<std::string> get_recent_projects() const;
void set_recent_projects(const std::vector<std::string>& recent_projects);
private:
// Map of section, name -> value
std::map<std::string, std::map<std::string, std::string>> m_storage;

View file

@ -1199,11 +1199,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
, m_bed(bed)
, m_camera(camera)
, m_view_toolbar(view_toolbar)
#if ENABLE_SVG_ICONS
, m_toolbar(GLToolbar::Normal, "Top")
#else
, m_toolbar(GLToolbar::Normal)
#endif // ENABLE_SVG_ICONS
, m_use_clipping_planes(false)
, m_sidebar_field("")
, m_keep_dirty(false)
@ -3422,12 +3418,6 @@ bool GLCanvas3D::_init_toolbar()
if (!m_toolbar.is_enabled())
return true;
#if !ENABLE_SVG_ICONS
ItemsIconsTexture::Metadata icons_data;
icons_data.filename = "toolbar.png";
icons_data.icon_size = 37;
#endif // !ENABLE_SVG_ICONS
BackgroundTexture::Metadata background_data;
background_data.filename = "toolbar_background.png";
background_data.left = 16;
@ -3435,11 +3425,7 @@ bool GLCanvas3D::_init_toolbar()
background_data.right = 16;
background_data.bottom = 16;
#if ENABLE_SVG_ICONS
if (!m_toolbar.init(background_data))
#else
if (!m_toolbar.init(icons_data, background_data))
#endif // ENABLE_SVG_ICONS
{
// unable to init the toolbar texture, disable it
m_toolbar.set_enabled(false);
@ -3456,9 +3442,7 @@ bool GLCanvas3D::_init_toolbar()
GLToolbarItem::Data item;
item.name = "add";
#if ENABLE_SVG_ICONS
item.icon_filename = "add.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Add...")) + " [" + GUI::shortkey_ctrl_prefix() + "I]";
item.sprite_id = 0;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); };
@ -3466,9 +3450,7 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "delete";
#if ENABLE_SVG_ICONS
item.icon_filename = "remove.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Delete")) + " [Del]";
item.sprite_id = 1;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); };
@ -3477,9 +3459,7 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "deleteall";
#if ENABLE_SVG_ICONS
item.icon_filename = "delete_all.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Delete all")) + " [" + GUI::shortkey_ctrl_prefix() + "Del]";
item.sprite_id = 2;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); };
@ -3488,9 +3468,7 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "arrange";
#if ENABLE_SVG_ICONS
item.icon_filename = "arrange.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Arrange")) + " [A]";
item.sprite_id = 3;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); };
@ -3502,9 +3480,7 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "copy";
#if ENABLE_SVG_ICONS
item.icon_filename = "copy.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Copy")) + " [" + GUI::shortkey_ctrl_prefix() + "C]";
item.sprite_id = 4;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); };
@ -3513,9 +3489,7 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "paste";
#if ENABLE_SVG_ICONS
item.icon_filename = "paste.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Paste")) + " [" + GUI::shortkey_ctrl_prefix() + "V]";
item.sprite_id = 5;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); };
@ -3527,9 +3501,7 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "more";
#if ENABLE_SVG_ICONS
item.icon_filename = "instance_add.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Add instance")) + " [+]";
item.sprite_id = 6;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); };
@ -3539,9 +3511,7 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "fewer";
#if ENABLE_SVG_ICONS
item.icon_filename = "instance_remove.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Remove instance")) + " [-]";
item.sprite_id = 7;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); };
@ -3554,9 +3524,7 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "splitobjects";
#if ENABLE_SVG_ICONS
item.icon_filename = "split_objects.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Split to objects"));
item.sprite_id = 8;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); };
@ -3566,9 +3534,7 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "splitvolumes";
#if ENABLE_SVG_ICONS
item.icon_filename = "split_parts.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Split to parts"));
item.sprite_id = 9;
item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); };
@ -3581,9 +3547,7 @@ bool GLCanvas3D::_init_toolbar()
return false;
item.name = "layersediting";
#if ENABLE_SVG_ICONS
item.icon_filename = "layers_white.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Layers editing"));
item.sprite_id = 10;
item.is_toggable = true;
@ -3921,9 +3885,6 @@ void GLCanvas3D::_render_overlays() const
_render_gizmos_overlay();
_render_warning_texture();
_render_legend_texture();
#if !ENABLE_SVG_ICONS
_resize_toolbars();
#endif // !ENABLE_SVG_ICONS
_render_toolbar();
_render_view_toolbar();
@ -4000,7 +3961,6 @@ void GLCanvas3D::_render_current_gizmo() const
void GLCanvas3D::_render_gizmos_overlay() const
{
#if ENABLE_SVG_ICONS
#if ENABLE_RETINA_GL
// m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor());
const float scale = m_retina_helper->get_scale_factor()*wxGetApp().toolbar_icon_scale();
@ -4011,14 +3971,12 @@ void GLCanvas3D::_render_gizmos_overlay() const
const float size = int(GLGizmosManager::Default_Icons_Size*wxGetApp().toolbar_icon_scale());
m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment
#endif /* __WXMSW__ */
#endif // ENABLE_SVG_ICONS
m_gizmos.render_overlay(*this, m_selection);
}
void GLCanvas3D::_render_toolbar() const
{
#if ENABLE_SVG_ICONS
#if ENABLE_RETINA_GL
// m_toolbar.set_scale(m_retina_helper->get_scale_factor());
const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(true);
@ -4073,20 +4031,12 @@ void GLCanvas3D::_render_toolbar() const
}
}
m_toolbar.set_position(top, left);
#else
#if ENABLE_RETINA_GL
m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
#else
m_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor());
#endif /* __WXMSW__ */
#endif // ENABLE_SVG_ICONS
m_toolbar.render(*this);
}
void GLCanvas3D::_render_view_toolbar() const
{
#if ENABLE_SVG_ICONS
#if ENABLE_RETINA_GL
// m_view_toolbar.set_scale(m_retina_helper->get_scale_factor());
const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale();
@ -4106,13 +4056,6 @@ void GLCanvas3D::_render_view_toolbar() const
float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom;
float left = -0.5f * (float)cnv_size.get_width() * inv_zoom;
m_view_toolbar.set_position(top, left);
#else
#if ENABLE_RETINA_GL
m_view_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
#else
m_view_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor());
#endif /* __WXMSW__ */
#endif // ENABLE_SVG_ICONS
m_view_toolbar.render(*this);
}
@ -5555,73 +5498,6 @@ bool GLCanvas3D::_is_any_volume_outside() const
return false;
}
#if !ENABLE_SVG_ICONS
void GLCanvas3D::_resize_toolbars() const
{
Size cnv_size = get_canvas_size();
float zoom = (float)m_camera.get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_RETINA_GL
m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
#else
m_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor());
#endif /* __WXMSW__ */
GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation();
switch (m_toolbar.get_layout_type())
{
default:
case GLToolbar::Layout::Horizontal:
{
// centers the toolbar on the top edge of the 3d scene
float top, left;
if (orientation == GLToolbar::Layout::Top)
{
top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
left = -0.5f * m_toolbar.get_width() * inv_zoom;
}
else
{
top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom;
left = -0.5f * m_toolbar.get_width() * inv_zoom;
}
m_toolbar.set_position(top, left);
break;
}
case GLToolbar::Layout::Vertical:
{
// centers the toolbar on the right edge of the 3d scene
float top, left;
if (orientation == GLToolbar::Layout::Left)
{
top = 0.5f * m_toolbar.get_height() * inv_zoom;
left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom;
}
else
{
top = 0.5f * m_toolbar.get_height() * inv_zoom;
left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom;
}
m_toolbar.set_position(top, left);
break;
}
}
#if ENABLE_RETINA_GL
m_view_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
#else
m_view_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor());
#endif /* __WXMSW__ */
// places the toolbar on the bottom-left corner of the 3d scene
float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom;
float left = -0.5f * (float)cnv_size.get_width() * inv_zoom;
m_view_toolbar.set_position(top, left);
}
#endif // !ENABLE_SVG_ICONS
void GLCanvas3D::_update_selection_from_hover()
{
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL);

View file

@ -728,10 +728,6 @@ private:
bool _is_any_volume_outside() const;
#if !ENABLE_SVG_ICONS
void _resize_toolbars() const;
#endif // !ENABLE_SVG_ICONS
// updates the selection from the content of m_hover_volume_idxs
void _update_selection_from_hover();

View file

@ -38,9 +38,7 @@ const GLToolbarItem::EnabledStateCallback GLToolbarItem::Default_Enabled_State_C
GLToolbarItem::Data::Data()
: name("")
#if ENABLE_SVG_ICONS
, icon_filename("")
#endif // ENABLE_SVG_ICONS
, tooltip("")
, sprite_id(-1)
, is_toggable(false)
@ -105,14 +103,6 @@ GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int
return uvs;
}
#if !ENABLE_SVG_ICONS
ItemsIconsTexture::Metadata::Metadata()
: filename("")
, icon_size(0)
{
}
#endif // !ENABLE_SVG_ICONS
BackgroundTexture::Metadata::Metadata()
: filename("")
, left(0)
@ -122,9 +112,7 @@ BackgroundTexture::Metadata::Metadata()
{
}
#if ENABLE_SVG_ICONS
const float GLToolbar::Default_Icons_Size = 40.0f;
#endif // ENABLE_SVG_ICONS
GLToolbar::Layout::Layout()
: type(Horizontal)
@ -134,31 +122,19 @@ GLToolbar::Layout::Layout()
, border(0.0f)
, separator_size(0.0f)
, gap_size(0.0f)
#if ENABLE_SVG_ICONS
, icons_size(Default_Icons_Size)
, scale(1.0f)
#else
, icons_scale(1.0f)
#endif // ENABLE_SVG_ICONS
, width(0.0f)
, height(0.0f)
, dirty(true)
{
}
#if ENABLE_SVG_ICONS
GLToolbar::GLToolbar(GLToolbar::EType type, const std::string& name)
#else
GLToolbar::GLToolbar(GLToolbar::EType type)
#endif // ENABLE_SVG_ICONS
: m_type(type)
#if ENABLE_SVG_ICONS
, m_name(name)
#endif // ENABLE_SVG_ICONS
, m_enabled(false)
#if ENABLE_SVG_ICONS
, m_icons_texture_dirty(true)
#endif // ENABLE_SVG_ICONS
, m_tooltip("")
{
}
@ -171,27 +147,13 @@ GLToolbar::~GLToolbar()
}
}
#if ENABLE_SVG_ICONS
bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
#else
bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture)
#endif // ENABLE_SVG_ICONS
{
#if ENABLE_SVG_ICONS
if (m_background_texture.texture.get_id() != 0)
return true;
std::string path = resources_dir() + "/icons/";
bool res = false;
#else
if (m_icons_texture.texture.get_id() != 0)
return true;
std::string path = resources_dir() + "/icons/";
bool res = !icons_texture.filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture.filename, false, true);
if (res)
m_icons_texture.metadata = icons_texture;
#endif // ENABLE_SVG_ICONS
if (!background_texture.filename.empty())
res = m_background_texture.texture.load_from_file(path + background_texture.filename, false, true);
@ -247,7 +209,6 @@ void GLToolbar::set_gap_size(float size)
m_layout.dirty = true;
}
#if ENABLE_SVG_ICONS
void GLToolbar::set_icons_size(float size)
{
if (m_layout.icons_size != size)
@ -267,13 +228,6 @@ void GLToolbar::set_scale(float scale)
m_icons_texture_dirty = true;
}
}
#else
void GLToolbar::set_icons_scale(float scale)
{
m_layout.icons_scale = scale;
m_layout.dirty = true;
}
#endif // ENABLE_SVG_ICONS
bool GLToolbar::is_enabled() const
{
@ -385,10 +339,8 @@ void GLToolbar::render(const GLCanvas3D& parent) const
if (!m_enabled || m_items.empty())
return;
#if ENABLE_SVG_ICONS
if (m_icons_texture_dirty)
generate_icons_texture();
#endif // ENABLE_SVG_ICONS
switch (m_layout.type)
{
@ -492,20 +444,12 @@ float GLToolbar::get_width_horizontal() const
float GLToolbar::get_width_vertical() const
{
#if ENABLE_SVG_ICONS
return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale;
#else
return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale;
#endif // ENABLE_SVG_ICONS
}
float GLToolbar::get_height_horizontal() const
{
#if ENABLE_SVG_ICONS
return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale;
#else
return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale;
#endif // ENABLE_SVG_ICONS
}
float GLToolbar::get_height_vertical() const
@ -515,7 +459,6 @@ float GLToolbar::get_height_vertical() const
float GLToolbar::get_main_size() const
{
#if ENABLE_SVG_ICONS
float size = 2.0f * m_layout.border;
for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
{
@ -531,25 +474,7 @@ float GLToolbar::get_main_size() const
if (m_items.size() > 1)
size += ((float)m_items.size() - 1.0f) * m_layout.gap_size;
size *= m_layout.scale;
#else
float size = 2.0f * m_layout.border * m_layout.icons_scale;
for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i)
{
if (!m_items[i]->is_visible())
continue;
if (m_items[i]->is_separator())
size += m_layout.separator_size * m_layout.icons_scale;
else
size += (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale;
}
if (m_items.size() > 1)
size += ((float)m_items.size() - 1.0f) * m_layout.gap_size * m_layout.icons_scale;
#endif // ENABLE_SVG_ICONS
return size;
return size * m_layout.scale;
}
void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent)
@ -609,20 +534,12 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
#else
float factor = m_layout.icons_scale * inv_zoom;
#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@ -714,20 +631,12 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
#else
float factor = m_layout.icons_scale * inv_zoom;
#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@ -831,20 +740,12 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
#else
float factor = m_layout.icons_scale * inv_zoom;
#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@ -914,20 +815,12 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = m_layout.scale * inv_zoom;
#else
float factor = m_layout.icons_scale * inv_zoom;
#endif // ENABLE_SVG_ICONS
Size cnv_size = parent.get_canvas_size();
Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom);
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@ -993,34 +886,15 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
{
#if ENABLE_SVG_ICONS
unsigned int tex_id = m_icons_texture.get_id();
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
#else
unsigned int tex_id = m_icons_texture.texture.get_id();
int tex_width = m_icons_texture.texture.get_width();
int tex_height = m_icons_texture.texture.get_height();
#endif // ENABLE_SVG_ICONS
#if !ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
#endif // !ENABLE_SVG_ICONS
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = inv_zoom * m_layout.scale;
#else
float factor = inv_zoom * m_layout.icons_scale;
#endif // ENABLE_SVG_ICONS
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@ -1121,10 +995,8 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
left += scaled_border;
top -= scaled_border;
#if ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
#endif // ENABLE_SVG_ICONS
// renders icons
for (const GLToolbarItem* item : m_items)
@ -1136,11 +1008,7 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
left += separator_stride;
else
{
#if ENABLE_SVG_ICONS
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
#else
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, m_icons_texture.metadata.icon_size);
#endif // ENABLE_SVG_ICONS
left += icon_stride;
}
}
@ -1148,34 +1016,15 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
void GLToolbar::render_vertical(const GLCanvas3D& parent) const
{
#if ENABLE_SVG_ICONS
unsigned int tex_id = m_icons_texture.get_id();
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
#else
unsigned int tex_id = m_icons_texture.texture.get_id();
int tex_width = m_icons_texture.texture.get_width();
int tex_height = m_icons_texture.texture.get_height();
#endif // ENABLE_SVG_ICONS
#if !ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
#endif // !ENABLE_SVG_ICONS
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_SVG_ICONS
float factor = inv_zoom * m_layout.scale;
#else
float factor = inv_zoom * m_layout.icons_scale;
#endif // ENABLE_SVG_ICONS
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_layout.icons_size * factor;
#else
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * factor;
#endif // ENABLE_SVG_ICONS
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float scaled_border = m_layout.border * factor;
@ -1276,10 +1125,8 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
left += scaled_border;
top -= scaled_border;
#if ENABLE_SVG_ICONS
if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
#endif // ENABLE_SVG_ICONS
// renders icons
for (const GLToolbarItem* item : m_items)
@ -1291,17 +1138,12 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
top -= separator_stride;
else
{
#if ENABLE_SVG_ICONS
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale));
#else
item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, m_icons_texture.metadata.icon_size);
#endif // ENABLE_SVG_ICONS
top -= icon_stride;
}
}
}
#if ENABLE_SVG_ICONS
bool GLToolbar::generate_icons_texture() const
{
std::string path = resources_dir() + "/icons/";
@ -1337,7 +1179,6 @@ bool GLToolbar::generate_icons_texture() const
return res;
}
#endif // ENABLE_SVG_ICONS
bool GLToolbar::update_items_visibility()
{

View file

@ -58,9 +58,7 @@ public:
struct Data
{
std::string name;
#if ENABLE_SVG_ICONS
std::string icon_filename;
#endif // ENABLE_SVG_ICONS
std::string tooltip;
unsigned int sprite_id;
bool is_toggable;
@ -88,9 +86,7 @@ public:
void set_state(EState state) { m_state = state; }
const std::string& get_name() const { return m_data.name; }
#if ENABLE_SVG_ICONS
const std::string& get_icon_filename() const { return m_data.icon_filename; }
#endif // ENABLE_SVG_ICONS
const std::string& get_tooltip() const { return m_data.tooltip; }
void do_action() { m_data.action_callback(); }
@ -118,27 +114,6 @@ private:
friend class GLToolbar;
};
#if !ENABLE_SVG_ICONS
// items icon textures are assumed to be square and all with the same size in pixels, no internal check is done
// icons are layed-out into the texture starting from the top-left corner in the same order as enum GLToolbarItem::EState
// from left to right
struct ItemsIconsTexture
{
struct Metadata
{
// path of the file containing the icons' texture
std::string filename;
// size of the square icons, in pixels
unsigned int icon_size;
Metadata();
};
GLTexture texture;
Metadata metadata;
};
#endif // !ENABLE_SVG_ICONS
struct BackgroundTexture
{
struct Metadata
@ -164,9 +139,7 @@ struct BackgroundTexture
class GLToolbar
{
public:
#if ENABLE_SVG_ICONS
static const float Default_Icons_Size;
#endif // ENABLE_SVG_ICONS
enum EType : unsigned char
{
@ -201,12 +174,8 @@ public:
float border;
float separator_size;
float gap_size;
#if ENABLE_SVG_ICONS
float icons_size;
float scale;
#else
float icons_scale;
#endif // ENABLE_SVG_ICONS
float width;
float height;
@ -219,16 +188,10 @@ private:
typedef std::vector<GLToolbarItem*> ItemsList;
EType m_type;
#if ENABLE_SVG_ICONS
std::string m_name;
#endif // ENABLE_SVG_ICONS
bool m_enabled;
#if ENABLE_SVG_ICONS
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
#else
ItemsIconsTexture m_icons_texture;
#endif // ENABLE_SVG_ICONS
BackgroundTexture m_background_texture;
mutable Layout m_layout;
@ -251,18 +214,10 @@ private:
std::string m_tooltip;
public:
#if ENABLE_SVG_ICONS
GLToolbar(EType type, const std::string& name);
#else
explicit GLToolbar(EType type);
#endif // ENABLE_SVG_ICONS
~GLToolbar();
#if ENABLE_SVG_ICONS
bool init(const BackgroundTexture::Metadata& background_texture);
#else
bool init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture);
#endif // ENABLE_SVG_ICONS
Layout::EType get_layout_type() const;
void set_layout_type(Layout::EType type);
@ -273,12 +228,8 @@ public:
void set_border(float border);
void set_separator_size(float size);
void set_gap_size(float size);
#if ENABLE_SVG_ICONS
void set_icons_size(float size);
void set_scale(float scale);
#else
void set_icons_scale(float scale);
#endif // ENABLE_SVG_ICONS
bool is_enabled() const;
void set_enabled(bool enable);
@ -297,7 +248,6 @@ public:
const std::string& get_tooltip() const { return m_tooltip; }
// returns true if any item changed its state
bool update_items_state();
@ -324,9 +274,7 @@ private:
void render_horizontal(const GLCanvas3D& parent) const;
void render_vertical(const GLCanvas3D& parent) const;
#if ENABLE_SVG_ICONS
bool generate_icons_texture() const;
#endif // ENABLE_SVG_ICONS
// returns true if any item changed its state
bool update_items_visibility();

View file

@ -17,6 +17,28 @@ namespace Slic3r
namespace GUI
{
// Helper function to be used by drop to bed button. Returns lowest point of this
// volume in world coordinate system.
static double get_volume_min_z(const GLVolume* volume)
{
const Transform3f& world_matrix = volume->world_matrix().cast<float>();
// need to get the ModelVolume pointer
const ModelObject* mo = wxGetApp().model_objects()->at(volume->composite_id.object_id);
const ModelVolume* mv = mo->volumes[volume->composite_id.volume_id];
const TriangleMesh& hull = mv->get_convex_hull();
float min_z = std::numeric_limits<float>::max();
for (const stl_facet& facet : hull.stl.facet_start) {
for (int i = 0; i < 3; ++ i)
min_z = std::min(min_z, Vec3f::UnitZ().dot(world_matrix * facet.vertex[i]));
}
return min_z;
}
static wxBitmapComboBox* create_word_local_combo(wxWindow *parent)
{
wxSize size(15 * wxGetApp().em_unit(), -1);
@ -185,7 +207,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
unsigned int axis_idx = (axis[0] - 'x'); // 0, 1 or 2
// We will add a button to toggle mirroring to each axis:
auto mirror_button = [=](wxWindow* parent) {
auto mirror_button = [this, mirror_btn_width, axis_idx, &label](wxWindow* parent) {
wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width);
auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off.png", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
btn->SetToolTip(wxString::Format(_(L("Toggle %s axis mirroring")), label));
@ -195,7 +217,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
btn->Bind(wxEVT_BUTTON, [this, axis_idx](wxCommandEvent &e) {
Axis axis = (Axis)(axis_idx + X);
if (m_mirror_buttons[axis_idx].second == mbHidden)
return;
@ -258,13 +280,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
return btn;
};
// Add reset scale button
auto reset_scale_button = [=](wxWindow* parent) {
auto reset_scale_button = [this](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
btn->SetToolTip(_(L("Reset scale")));
m_reset_scale_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
change_scale_value(0, 100.);
change_scale_value(1, 100.);
change_scale_value(2, 100.);
@ -275,13 +297,13 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
}
else if (option_name == "Rotation") {
// Add reset rotation button
auto reset_rotation_button = [=](wxWindow* parent) {
auto reset_rotation_button = [this](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
btn->SetToolTip(_(L("Reset rotation")));
m_reset_rotation_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
@ -310,6 +332,34 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
};
line.append_widget(reset_rotation_button);
}
else if (option_name == "Position") {
// Add drop to bed button
auto drop_to_bed_button = [=](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "drop_to_bed.png"));
btn->SetToolTip(_(L("Drop to bed")));
m_drop_to_bed_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
// ???
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
if (selection.is_single_volume() || selection.is_single_modifier()) {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
const Geometry::Transformation& instance_trafo = volume->get_instance_transformation();
Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume));
change_position_value(0, diff.x());
change_position_value(1, diff.y());
change_position_value(2, diff.z());
}
});
return sizer;
};
line.append_widget(drop_to_bed_button);
}
// Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment
else if (option_name == "Size") {
line.near_label_widget = [this](wxWindow* parent) {
@ -536,11 +586,13 @@ void ObjectManipulation::update_reset_buttons_visibility()
bool show_rotation = false;
bool show_scale = false;
bool show_drop_to_bed = false;
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Vec3d rotation;
Vec3d scale;
double min_z = 0.;
if (selection.is_single_full_instance()) {
rotation = volume->get_instance_rotation();
@ -549,14 +601,17 @@ void ObjectManipulation::update_reset_buttons_visibility()
else {
rotation = volume->get_volume_rotation();
scale = volume->get_volume_scaling_factor();
min_z = get_volume_min_z(volume);
}
show_rotation = !rotation.isApprox(Vec3d::Zero());
show_scale = !scale.isApprox(Vec3d::Ones());
show_drop_to_bed = (std::abs(min_z) > EPSILON);
}
wxGetApp().CallAfter([this, show_rotation, show_scale]{
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed]{
m_reset_rotation_button->Show(show_rotation);
m_reset_scale_button->Show(show_scale);
m_drop_to_bed_button->Show(show_drop_to_bed);
});
}
@ -869,6 +924,7 @@ void ObjectManipulation::msw_rescale()
m_mirror_bitmap_hidden.msw_rescale();
m_reset_scale_button->msw_rescale();
m_reset_rotation_button->msw_rescale();
m_drop_to_bed_button->msw_rescale();
get_og()->msw_rescale();
}

View file

@ -56,6 +56,7 @@ class ObjectManipulation : public OG_Settings
// Non-owning pointers to the reset buttons, so we can hide and show them.
ScalableButton* m_reset_scale_button = nullptr;
ScalableButton* m_reset_rotation_button = nullptr;
ScalableButton* m_drop_to_bed_button = nullptr;
// Mirroring buttons and their current state
enum MirrorButtonState {

View file

@ -132,18 +132,12 @@ void GLGizmoBase::Grabber::render_face(float half_size) const
glsafe(::glEnd());
}
#if ENABLE_SVG_ICONS
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
#else
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id)
#endif // ENABLE_SVG_ICONS
: m_parent(parent)
, m_group_id(-1)
, m_state(Off)
, m_shortcut_key(0)
#if ENABLE_SVG_ICONS
, m_icon_filename(icon_filename)
#endif // ENABLE_SVG_ICONS
, m_sprite_id(sprite_id)
, m_hover_id(-1)
, m_dragging(false)

View file

@ -89,9 +89,7 @@ protected:
int m_group_id;
EState m_state;
int m_shortcut_key;
#if ENABLE_SVG_ICONS
std::string m_icon_filename;
#endif // ENABLE_SVG_ICONS
unsigned int m_sprite_id;
int m_hover_id;
bool m_dragging;
@ -102,11 +100,7 @@ protected:
ImGuiWrapper* m_imgui;
public:
#if ENABLE_SVG_ICONS
GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoBase() {}
bool init() { return on_init(); }
@ -122,9 +116,7 @@ public:
int get_shortcut_key() const { return m_shortcut_key; }
void set_shortcut_key(int key) { m_shortcut_key = key; }
#if ENABLE_SVG_ICONS
const std::string& get_icon_filename() const { return m_icon_filename; }
#endif // ENABLE_SVG_ICONS
bool is_activable(const Selection& selection) const { return on_is_activable(selection); }
bool is_selectable() const { return on_is_selectable(); }

View file

@ -19,13 +19,8 @@ const double GLGizmoCut::Offset = 10.0;
const double GLGizmoCut::Margin = 20.0;
const std::array<float, 3> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 };
#if ENABLE_SVG_ICONS
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_cut_z(0.0)
, m_max_z(0.0)
, m_keep_upper(true)

View file

@ -23,11 +23,7 @@ class GLGizmoCut : public GLGizmoBase
bool m_rotate_lower;
public:
#if ENABLE_SVG_ICONS
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
protected:
virtual bool on_init();

View file

@ -9,13 +9,8 @@ namespace Slic3r {
namespace GUI {
#if ENABLE_SVG_ICONS
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_normal(Vec3d::Zero())
, m_starting_center(Vec3d::Zero())
{

View file

@ -37,11 +37,7 @@ private:
bool is_plane_update_necessary() const;
public:
#if ENABLE_SVG_ICONS
GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
void set_flattening_data(const ModelObject* model_object);
Vec3d get_flattening_normal() const;

View file

@ -10,13 +10,8 @@ namespace GUI {
const double GLGizmoMove3D::Offset = 10.0;
#if ENABLE_SVG_ICONS
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_displacement(Vec3d::Zero())
, m_snap_step(1.0)
, m_starting_drag_position(Vec3d::Zero())

View file

@ -22,11 +22,7 @@ class GLGizmoMove3D : public GLGizmoBase
GLUquadricObj* m_quadric;
public:
#if ENABLE_SVG_ICONS
GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoMove3D();
double get_snap_step(double step) const { return m_snap_step; }

View file

@ -19,11 +19,7 @@ const unsigned int GLGizmoRotate::SnapRegionsCount = 8;
const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius
GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
#if ENABLE_SVG_ICONS
: GLGizmoBase(parent, "", -1)
#else
: GLGizmoBase(parent, -1)
#endif // ENABLE_SVG_ICONS
, m_axis(axis)
, m_angle(0.0)
, m_quadric(nullptr)
@ -40,11 +36,7 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
}
GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other)
#if ENABLE_SVG_ICONS
: GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id)
#else
: GLGizmoBase(other.m_parent, other.m_sprite_id)
#endif // ENABLE_SVG_ICONS
, m_axis(other.m_axis)
, m_angle(other.m_angle)
, m_quadric(nullptr)
@ -417,13 +409,8 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons
return transform(mouse_ray, m).intersect_plane(0.0);
}
#if ENABLE_SVG_ICONS
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
{
m_gizmos.emplace_back(parent, GLGizmoRotate::X);
m_gizmos.emplace_back(parent, GLGizmoRotate::Y);

View file

@ -76,11 +76,7 @@ class GLGizmoRotate3D : public GLGizmoBase
std::vector<GLGizmoRotate> m_gizmos;
public:
#if ENABLE_SVG_ICONS
GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); }
void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); }

View file

@ -13,13 +13,8 @@ namespace GUI {
const float GLGizmoScale3D::Offset = 5.0f;
#if ENABLE_SVG_ICONS
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_scale(Vec3d::Ones())
, m_offset(Vec3d::Zero())
, m_snap_step(0.05)

View file

@ -32,11 +32,7 @@ class GLGizmoScale3D : public GLGizmoBase
StartingData m_starting;
public:
#if ENABLE_SVG_ICONS
GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
double get_snap_step(double step) const { return m_snap_step; }
void set_snap_step(double step) { m_snap_step = step; }

View file

@ -19,13 +19,8 @@
namespace Slic3r {
namespace GUI {
#if ENABLE_SVG_ICONS
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_quadric(nullptr)
, m_its(nullptr)
{

View file

@ -58,11 +58,7 @@ private:
};
public:
#if ENABLE_SVG_ICONS
GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoSlaSupports();
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);

View file

@ -12,17 +12,12 @@
namespace Slic3r {
namespace GUI {
#if ENABLE_SVG_ICONS
const float GLGizmosManager::Default_Icons_Size = 64;
#endif // ENABLE_SVG_ICONS
const float GLGizmosManager::Default_Icons_Size = 64;
GLGizmosManager::GLGizmosManager()
: m_enabled(false)
#if ENABLE_SVG_ICONS
, m_icons_texture_dirty(true)
#endif // ENABLE_SVG_ICONS
, m_current(Undefined)
#if ENABLE_SVG_ICONS
, m_overlay_icons_size(Default_Icons_Size)
, m_overlay_scale(1.0f)
, m_overlay_border(5.0f)
@ -30,11 +25,6 @@ GLGizmosManager::GLGizmosManager()
, m_tooltip("")
{
}
#else
{
set_overlay_scale(1.0);
}
#endif // ENABLE_SVG_ICONS
GLGizmosManager::~GLGizmosManager()
{
@ -43,20 +33,6 @@ GLGizmosManager::~GLGizmosManager()
bool GLGizmosManager::init(GLCanvas3D& parent)
{
#if !ENABLE_SVG_ICONS
m_icons_texture.metadata.filename = "gizmos.png";
m_icons_texture.metadata.icon_size = 64;
if (!m_icons_texture.metadata.filename.empty())
{
if (!m_icons_texture.texture.load_from_file(resources_dir() + "/icons/" + m_icons_texture.metadata.filename, false, true))
{
reset();
return false;
}
}
#endif // !ENABLE_SVG_ICONS
m_background_texture.metadata.filename = "toolbar_background.png";
m_background_texture.metadata.left = 16;
m_background_texture.metadata.top = 16;
@ -72,11 +48,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
}
}
#if ENABLE_SVG_ICONS
GLGizmoBase* gizmo = new GLGizmoMove3D(parent, "move.svg", 0);
#else
GLGizmoBase* gizmo = new GLGizmoMove3D(parent, 0);
#endif // ENABLE_SVG_ICONS
if (gizmo == nullptr)
return false;
@ -85,11 +57,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
m_gizmos.insert(GizmosMap::value_type(Move, gizmo));
#if ENABLE_SVG_ICONS
gizmo = new GLGizmoScale3D(parent, "scale.svg", 1);
#else
gizmo = new GLGizmoScale3D(parent, 1);
#endif // ENABLE_SVG_ICONS
if (gizmo == nullptr)
return false;
@ -98,11 +66,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
m_gizmos.insert(GizmosMap::value_type(Scale, gizmo));
#if ENABLE_SVG_ICONS
gizmo = new GLGizmoRotate3D(parent, "rotate.svg", 2);
#else
gizmo = new GLGizmoRotate3D(parent, 2);
#endif // ENABLE_SVG_ICONS
if (gizmo == nullptr)
{
reset();
@ -117,11 +81,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo));
#if ENABLE_SVG_ICONS
gizmo = new GLGizmoFlatten(parent, "place.svg", 3);
#else
gizmo = new GLGizmoFlatten(parent, 3);
#endif // ENABLE_SVG_ICONS
if (gizmo == nullptr)
return false;
@ -132,11 +92,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo));
#if ENABLE_SVG_ICONS
gizmo = new GLGizmoCut(parent, "cut.svg", 4);
#else
gizmo = new GLGizmoCut(parent, 4);
#endif // ENABLE_SVG_ICONS
if (gizmo == nullptr)
return false;
@ -147,11 +103,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
m_gizmos.insert(GizmosMap::value_type(Cut, gizmo));
#if ENABLE_SVG_ICONS
gizmo = new GLGizmoSlaSupports(parent, "sla_supports.svg", 5);
#else
gizmo = new GLGizmoSlaSupports(parent, 5);
#endif // ENABLE_SVG_ICONS
if (gizmo == nullptr)
return false;
@ -165,7 +117,6 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
return true;
}
#if ENABLE_SVG_ICONS
void GLGizmosManager::set_overlay_icon_size(float size)
{
if (m_overlay_icons_size != size)
@ -174,21 +125,14 @@ void GLGizmosManager::set_overlay_icon_size(float size)
m_icons_texture_dirty = true;
}
}
#endif // ENABLE_SVG_ICONS
void GLGizmosManager::set_overlay_scale(float scale)
{
#if ENABLE_SVG_ICONS
if (m_overlay_scale != scale)
{
m_overlay_scale = scale;
m_icons_texture_dirty = true;
}
#else
m_overlay_icons_scale = scale;
m_overlay_border = 5.0f * scale;
m_overlay_gap_y = 5.0f * scale;
#endif // ENABLE_SVG_ICONS
}
void GLGizmosManager::refresh_on_off_state(const Selection& selection)
@ -526,10 +470,8 @@ void GLGizmosManager::render_overlay(const GLCanvas3D& canvas, const Selection&
if (!m_enabled)
return;
#if ENABLE_SVG_ICONS
if (m_icons_texture_dirty)
generate_icons_texture();
#endif // ENABLE_SVG_ICONS
do_render_overlay(canvas, selection);
}
@ -935,11 +877,7 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio
float height = get_total_overlay_height();
float width = get_total_overlay_width();
#if ENABLE_SVG_ICONS
float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom;
#else
float scaled_border = m_overlay_border * inv_zoom;
#endif // ENABLE_SVG_ICONS
float top_x = (-0.5f * cnv_w) * inv_zoom;
float top_y = (0.5f * height) * inv_zoom;
@ -1015,7 +953,6 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio
}
}
#if ENABLE_SVG_ICONS
top_x += scaled_border;
top_y -= scaled_border;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale * inv_zoom;
@ -1027,21 +964,9 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio
unsigned int tex_height = m_icons_texture.get_height();
float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f;
float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f;
#else
top_x += m_overlay_border * inv_zoom;
top_y -= m_overlay_border * inv_zoom;
float scaled_gap_y = m_overlay_gap_y * inv_zoom;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale * inv_zoom;
unsigned int icons_texture_id = m_icons_texture.texture.get_id();
unsigned int texture_size = m_icons_texture.texture.get_width();
float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f;
#endif // ENABLE_SVG_ICONS
#if ENABLE_SVG_ICONS
if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
#endif // ENABLE_SVG_ICONS
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
@ -1051,78 +976,44 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio
unsigned int sprite_id = it->second->get_sprite_id();
GLGizmoBase::EState state = it->second->get_state();
#if ENABLE_SVG_ICONS
float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width;
float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height;
float v_top = sprite_id * v_icon_size;
float u_left = state * u_icon_size;
float v_bottom = v_top + v_icon_size;
float u_right = u_left + u_icon_size;
#else
float uv_icon_size = (float)m_icons_texture.metadata.icon_size * inv_texture_size;
float v_top = sprite_id * uv_icon_size;
float u_left = state * uv_icon_size;
float v_bottom = v_top + uv_icon_size;
float u_right = u_left + uv_icon_size;
#endif // ENABLE_SVG_ICONS
GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
if (it->second->get_state() == GLGizmoBase::On) {
float toolbar_top = (float)cnv_h - canvas.get_view_toolbar_height();
#if ENABLE_SVG_ICONS
it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
#else
it->second->render_input_window(2.0f * m_overlay_border + scaled_icons_size * zoom, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
#endif // ENABLE_SVG_ICONS
}
#if ENABLE_SVG_ICONS
top_y -= scaled_stride_y;
#else
top_y -= (scaled_icons_size + scaled_gap_y);
#endif // ENABLE_SVG_ICONS
}
}
float GLGizmosManager::get_total_overlay_height() const
{
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
float scaled_border = m_overlay_border * m_overlay_scale;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
float height = 2.0f * scaled_border;
#else
float height = 2.0f * m_overlay_border;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
#endif // ENABLE_SVG_ICONS
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
if ((it->second == nullptr) || !it->second->is_selectable())
continue;
#if ENABLE_SVG_ICONS
height += scaled_stride_y;
#else
height += (scaled_icons_size + m_overlay_gap_y);
#endif // ENABLE_SVG_ICONS
}
#if ENABLE_SVG_ICONS
return height - scaled_gap_y;
#else
return height - m_overlay_gap_y;
#endif // ENABLE_SVG_ICONS
}
float GLGizmosManager::get_total_overlay_width() const
{
#if ENABLE_SVG_ICONS
return (2.0f * m_overlay_border + m_overlay_icons_size) * m_overlay_scale;
#else
return (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale + 2.0f * m_overlay_border;
#endif // ENABLE_SVG_ICONS
}
GLGizmoBase* GLGizmosManager::get_current() const
@ -1131,7 +1022,6 @@ GLGizmoBase* GLGizmosManager::get_current() const
return (it != m_gizmos.end()) ? it->second : nullptr;
}
#if ENABLE_SVG_ICONS
bool GLGizmosManager::generate_icons_texture() const
{
std::string path = resources_dir() + "/icons/";
@ -1157,7 +1047,6 @@ bool GLGizmosManager::generate_icons_texture() const
return res;
}
#endif // ENABLE_SVG_ICONS
void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection)
{
@ -1167,27 +1056,18 @@ void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d&
float cnv_h = (float)canvas.get_canvas_size().get_height();
float height = get_total_overlay_height();
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
float scaled_border = m_overlay_border * m_overlay_scale;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
float top_y = 0.5f * (cnv_h - height) + scaled_border;
#else
float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
#endif // ENABLE_SVG_ICONS
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
if ((it->second == nullptr) || !it->second->is_selectable())
continue;
#if ENABLE_SVG_ICONS
bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
#else
bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
#endif // ENABLE_SVG_ICONS
if (it->second->is_activable(selection) && inside)
{
if ((it->second->get_state() == GLGizmoBase::On))
@ -1204,11 +1084,7 @@ void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d&
else
it->second->set_state(GLGizmoBase::Off);
#if ENABLE_SVG_ICONS
top_y += scaled_stride_y;
#else
top_y += (scaled_icons_size + m_overlay_gap_y);
#endif // ENABLE_SVG_ICONS
}
GizmosMap::iterator it = m_gizmos.find(m_current);
@ -1227,38 +1103,25 @@ std::string GLGizmosManager::update_hover_state(const GLCanvas3D& canvas, const
float cnv_h = (float)canvas.get_canvas_size().get_height();
float height = get_total_overlay_height();
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
float scaled_border = m_overlay_border * m_overlay_scale;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
float top_y = 0.5f * (cnv_h - height) + scaled_border;
#else
float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
#endif // ENABLE_SVG_ICONS
for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
if ((it->second == nullptr) || !it->second->is_selectable())
continue;
#if ENABLE_SVG_ICONS
bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
#else
bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size);
#endif // ENABLE_SVG_ICONS
if (inside)
name = it->second->get_name();
if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On))
it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off);
#if ENABLE_SVG_ICONS
top_y += scaled_stride_y;
#else
top_y += (scaled_icons_size + m_overlay_gap_y);
#endif // ENABLE_SVG_ICONS
}
return name;
@ -1272,34 +1135,21 @@ bool GLGizmosManager::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec
float cnv_h = (float)canvas.get_canvas_size().get_height();
float height = get_total_overlay_height();
#if ENABLE_SVG_ICONS
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
float scaled_border = m_overlay_border * m_overlay_scale;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
float top_y = 0.5f * (cnv_h - height) + scaled_border;
#else
float top_y = 0.5f * (cnv_h - height) + m_overlay_border;
float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale;
#endif // ENABLE_SVG_ICONS
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
if ((it->second == nullptr) || !it->second->is_selectable())
continue;
#if ENABLE_SVG_ICONS
if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size))
#else
if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size))
#endif // ENABLE_SVG_ICONS
return true;
#if ENABLE_SVG_ICONS
top_y += scaled_stride_y;
#else
top_y += (scaled_icons_size + m_overlay_gap_y);
#endif // ENABLE_SVG_ICONS
}
return false;

View file

@ -46,9 +46,7 @@ public:
class GLGizmosManager
{
public:
#if ENABLE_SVG_ICONS
static const float Default_Icons_Size;
#endif // ENABLE_SVG_ICONS
enum EType : unsigned char
{
@ -66,21 +64,13 @@ private:
bool m_enabled;
typedef std::map<EType, GLGizmoBase*> GizmosMap;
GizmosMap m_gizmos;
#if ENABLE_SVG_ICONS
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
#else
ItemsIconsTexture m_icons_texture;
#endif // ENABLE_SVG_ICONS
BackgroundTexture m_background_texture;
EType m_current;
#if ENABLE_SVG_ICONS
float m_overlay_icons_size;
float m_overlay_scale;
#else
float m_overlay_icons_scale;
#endif // ENABLE_SVG_ICONS
float m_overlay_border;
float m_overlay_gap_y;
@ -109,9 +99,7 @@ public:
bool is_enabled() const { return m_enabled; }
void set_enabled(bool enable) { m_enabled = enable; }
#if ENABLE_SVG_ICONS
void set_overlay_icon_size(float size);
#endif // ENABLE_SVG_ICONS
void set_overlay_scale(float scale);
void refresh_on_off_state(const Selection& selection);
@ -173,9 +161,7 @@ private:
GLGizmoBase* get_current() const;
#if ENABLE_SVG_ICONS
bool generate_icons_texture() const;
#endif // ENABLE_SVG_ICONS
void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos);

View file

@ -35,6 +35,7 @@ namespace GUI {
MainFrame::MainFrame() :
DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"),
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
, m_recent_projects(9)
{
// Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
wxGetApp().update_fonts(this);
@ -383,6 +384,40 @@ void MainFrame::init_menubar()
append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, menu_icon("open"), nullptr,
[this](){return m_plater != nullptr; }, this);
wxMenu* recent_projects_menu = new wxMenu();
wxMenuItem* recent_projects_submenu = append_submenu(fileMenu, recent_projects_menu, wxID_ANY, _(L("Recent projects")), "");
m_recent_projects.UseMenu(recent_projects_menu);
Bind(wxEVT_MENU, [this](wxCommandEvent& evt) {
size_t file_id = evt.GetId() - wxID_FILE1;
wxString filename = m_recent_projects.GetHistoryFile(file_id);
if (wxFileExists(filename))
m_plater->load_project(filename);
else
{
wxMessageDialog msg(this, _(L("The selected project is no more available")), _(L("Error")));
msg.ShowModal();
m_recent_projects.RemoveFileFromHistory(file_id);
std::vector<std::string> recent_projects;
size_t count = m_recent_projects.GetCount();
for (size_t i = 0; i < count; ++i)
{
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
}
wxGetApp().app_config->set_recent_projects(recent_projects);
wxGetApp().app_config->save();
}
}, wxID_FILE1, wxID_FILE9);
std::vector<std::string> recent_projects = wxGetApp().app_config->get_recent_projects();
for (const std::string& project : recent_projects)
{
m_recent_projects.AddFileToHistory(from_u8(project));
}
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId());
append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, menu_icon("save"), nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
@ -1046,6 +1081,23 @@ void MainFrame::on_config_changed(DynamicPrintConfig* config) const
m_plater->on_config_change(*config); // propagate config change events to the plater
}
void MainFrame::add_to_recent_projects(const wxString& filename)
{
if (wxFileExists(filename))
{
m_recent_projects.AddFileToHistory(filename);
std::vector<std::string> recent_projects;
size_t count = m_recent_projects.GetCount();
for (size_t i = 0; i < count; ++i)
{
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
}
wxGetApp().app_config->set_recent_projects(recent_projects);
wxGetApp().app_config->save();
}
}
//
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
void MainFrame::update_ui_from_settings()

View file

@ -6,6 +6,7 @@
#include <wx/frame.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/filehistory.h>
#include <string>
#include <map>
@ -84,6 +85,8 @@ class MainFrame : public DPIFrame
// vector of a MenuBar items changeable in respect to printer technology
std::vector<wxMenuItem*> m_changeable_menu_items;
wxFileHistory m_recent_projects;
protected:
virtual void on_dpi_changed(const wxRect &suggested_rect);
@ -121,6 +124,8 @@ public:
// Propagate changed configuration from the Tab to the Platter and save changes to the AppConfig
void on_config_changed(DynamicPrintConfig* cfg) const ;
void add_to_recent_projects(const wxString& filename);
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
Plater* m_plater { nullptr };

View file

@ -266,7 +266,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
is_sizer_field(field) ?
v_sizer->Add(field->getSizer(), 0, wxEXPAND) :
v_sizer->Add(field->getWindow(), 0, wxEXPAND);
return;
break;//return;
}
is_sizer_field(field) ?
@ -300,7 +300,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
{
// extra widget for non-staticbox option group (like for the frequently used parameters on the sidebar) should be wxALIGN_RIGHT
const auto v_sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(v_sizer, 1, wxEXPAND);
sizer->Add(v_sizer, option_set.size() == 1 ? 0 : 1, wxEXPAND);
v_sizer->Add(extra_widget(this->ctrl_parent()), 0, wxALIGN_RIGHT);
return;
}

View file

@ -162,6 +162,12 @@ public:
void clear_fields_except_of(const std::vector<std::string> left_fields);
void hide_labels() {
label_width = 0;
m_grid_sizer->SetCols(m_grid_sizer->GetEffectiveColsCount()-1);
static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(!extra_column ? 0 : 1);
}
OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false,
column_t extra_clmn = nullptr) :
m_parent(_parent), title(title),

View file

@ -245,6 +245,7 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 *
last_selected(wxNOT_FOUND),
m_em_unit(wxGetApp().em_unit())
{
SetFont(wxGetApp().normal_font());
Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &evt) {
auto selected_item = this->GetSelection();
@ -372,24 +373,36 @@ class FreqChangedParams : public OG_Settings
wxSizer* m_sizer {nullptr};
std::shared_ptr<ConfigOptionsGroup> m_og_sla;
std::vector<ScalableButton*> m_empty_buttons;
public:
FreqChangedParams(wxWindow* parent, const int label_width);
FreqChangedParams(wxWindow* parent);
~FreqChangedParams() {}
wxButton* get_wiping_dialog_button() { return m_wiping_dialog_button; }
wxSizer* get_sizer() override;
ConfigOptionsGroup* get_og(const bool is_fff);
void Show(const bool is_fff);
void msw_rescale();
};
FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
void FreqChangedParams::msw_rescale()
{
m_og->msw_rescale();
m_og_sla->msw_rescale();
for (auto btn: m_empty_buttons)
btn->msw_rescale();
}
FreqChangedParams::FreqChangedParams(wxWindow* parent) :
OG_Settings(parent, false)
{
DynamicPrintConfig* config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
// Frequently changed parameters for FFF_technology
m_og->set_config(config);
m_og->label_width = label_width == 0 ? 1 : label_width;
m_og->hide_labels();
m_og->m_on_change = [config, this](t_config_option_key opt_key, boost::any value) {
Tab* tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT);
@ -461,6 +474,20 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
Option option = Option(support_def, "support");
option.opt.full_width = true;
line.append_option(option);
/* Not a best solution, but
* Temporary workaround for right border alignment
*/
auto empty_widget = [this] (wxWindow* parent) {
auto sizer = new wxBoxSizer(wxHORIZONTAL);
auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent.png", wxEmptyString,
wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, int(0.3 * wxGetApp().em_unit()));
m_empty_buttons.push_back(btn);
return sizer;
};
line.append_widget(empty_widget);
m_og->append_line(line);
@ -469,7 +496,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
option = m_og->get_option("fill_density");
option.opt.label = L("Infill");
option.opt.width = 7/*6*/;
option.opt.sidetext = " ";
option.opt.sidetext = " ";
line.append_option(option);
m_brim_width = config->opt_float("brim_width");
@ -480,13 +507,14 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
def.gui_type = "";
def.set_default_value(new ConfigOptionBool{ m_brim_width > 0.0 ? true : false });
option = Option(def, "brim");
option.opt.sidetext = " ";
option.opt.sidetext = "";
line.append_option(option);
auto wiping_dialog_btn = [config, this](wxWindow* parent) {
m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
m_wiping_dialog_button->SetFont(wxGetApp().normal_font());
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_wiping_dialog_button);
sizer->Add(m_wiping_dialog_button, 0, wxALIGN_CENTER_VERTICAL);
m_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e)
{
auto &config = wxGetApp().preset_bundle->project_config;
@ -503,6 +531,13 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
wxPostEvent(parent, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, parent));
}
}));
auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_transparent.png", wxEmptyString,
wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
sizer->Add(btn , 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT,
int(0.3 * wxGetApp().em_unit()));
m_empty_buttons.push_back(btn);
return sizer;
};
line.append_widget(wiping_dialog_btn);
@ -512,9 +547,9 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
// Frequently changed parameters for SLA_technology
m_og_sla = std::make_shared<ConfigOptionsGroup>(parent, "");
m_og_sla->hide_labels();
DynamicPrintConfig* config_sla = &wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
m_og_sla->set_config(config_sla);
m_og_sla->label_width = label_width == 0 ? 1 : label_width;
m_og_sla->m_on_change = [config_sla, this](t_config_option_key opt_key, boost::any value) {
Tab* tab = wxGetApp().get_tab(Preset::TYPE_SLA_PRINT);
@ -552,7 +587,8 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
support_def_sla.enum_labels.erase(support_def_sla.enum_labels.begin() + 2);
option = Option(support_def_sla, "support");
option.opt.full_width = true;
line.append_option(option);
line.append_option(option);
line.append_widget(empty_widget);
m_og_sla->append_line(line);
line = Line{ "", "" };
@ -733,13 +769,12 @@ Sidebar::Sidebar(Plater *parent)
init_combo(&p->combo_printer, _(L("Printer")), Preset::TYPE_PRINTER, false);
const int margin_5 = int(0.5*wxGetApp().em_unit());// 5;
const int margin_10 = 10;//int(1.5*wxGetApp().em_unit());// 15;
p->sizer_params = new wxBoxSizer(wxVERTICAL);
// Frequently changed parameters
p->frequently_changed_parameters = new FreqChangedParams(p->scrolled, 0/*label_width*/);
p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, margin_10);
p->frequently_changed_parameters = new FreqChangedParams(p->scrolled);
p->sizer_params->Add(p->frequently_changed_parameters->get_sizer(), 0, wxEXPAND | wxTOP | wxBOTTOM, wxOSX ? 1 : margin_5);
// Object List
p->object_list = new ObjectList(p->scrolled);
@ -946,9 +981,7 @@ void Sidebar::msw_rescale()
// ... then refill them and set min size to correct layout of the sidebar
update_all_preset_comboboxes();
p->frequently_changed_parameters->get_og(true)->msw_rescale();
p->frequently_changed_parameters->get_og(false)->msw_rescale();
p->frequently_changed_parameters->msw_rescale();
p->object_list->msw_rescale();
p->object_manipulation->msw_rescale();
p->object_settings->msw_rescale();
@ -1132,10 +1165,20 @@ void Sidebar::show_sliced_info_sizer(const bool show)
if (ps.estimated_normal_print_time != "N/A") {
new_label += wxString::Format("\n - %s", _(L("normal mode")));
info_text += wxString::Format("\n%s", ps.estimated_normal_print_time);
for (int i = (int)ps.estimated_normal_color_print_times.size() - 1; i >= 0; --i)
{
new_label += wxString::Format("\n - %s%d", _(L("Color ")), i + 1);
info_text += wxString::Format("\n%s", ps.estimated_normal_color_print_times[i]);
}
}
if (ps.estimated_silent_print_time != "N/A") {
new_label += wxString::Format("\n - %s", _(L("stealth mode")));
info_text += wxString::Format("\n%s", ps.estimated_silent_print_time);
for (int i = (int)ps.estimated_normal_color_print_times.size() - 1; i >= 0; --i)
{
new_label += wxString::Format("\n - %s%d", _(L("Color ")), i + 1);
info_text += wxString::Format("\n%s", ps.estimated_normal_color_print_times[i]);
}
}
p->sliced_info->SetTextAndShow(siEstimatedTime, info_text, new_label);
}
@ -1693,11 +1736,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
}))
, sidebar(new Sidebar(q))
, delayed_scene_refresh(false)
#if ENABLE_SVG_ICONS
, view_toolbar(GLToolbar::Radio, "View")
#else
, view_toolbar(GLToolbar::Radio)
#endif // ENABLE_SVG_ICONS
, m_project_filename(wxEmptyString)
{
this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font());
@ -3238,6 +3277,9 @@ void Plater::priv::set_project_filename(const wxString& filename)
m_project_filename = from_path(full_path);
wxGetApp().mainframe->update_title();
if (!filename.empty())
wxGetApp().mainframe->add_to_recent_projects(filename);
}
bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/)
@ -3352,12 +3394,6 @@ bool Plater::priv::complit_init_part_menu()
void Plater::priv::init_view_toolbar()
{
#if !ENABLE_SVG_ICONS
ItemsIconsTexture::Metadata icons_data;
icons_data.filename = "view_toolbar.png";
icons_data.icon_size = 64;
#endif // !ENABLE_SVG_ICONS
BackgroundTexture::Metadata background_data;
background_data.filename = "toolbar_background.png";
background_data.left = 16;
@ -3365,11 +3401,7 @@ void Plater::priv::init_view_toolbar()
background_data.right = 16;
background_data.bottom = 16;
#if ENABLE_SVG_ICONS
if (!view_toolbar.init(background_data))
#else
if (!view_toolbar.init(icons_data, background_data))
#endif // ENABLE_SVG_ICONS
return;
view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom);
@ -3379,9 +3411,7 @@ void Plater::priv::init_view_toolbar()
GLToolbarItem::Data item;
item.name = "3D";
#if ENABLE_SVG_ICONS
item.icon_filename = "editor.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("3D editor view")) + " [" + GUI::shortkey_ctrl_prefix() + "5]";
item.sprite_id = 0;
item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); };
@ -3390,9 +3420,7 @@ void Plater::priv::init_view_toolbar()
return;
item.name = "Preview";
#if ENABLE_SVG_ICONS
item.icon_filename = "preview.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = _utf8(L("Preview")) + " [" + GUI::shortkey_ctrl_prefix() + "6]";
item.sprite_id = 1;
item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); };
@ -3565,15 +3593,19 @@ void Plater::load_project()
{
wxString input_file;
wxGetApp().load_project(this, input_file);
load_project(input_file);
}
if (input_file.empty())
void Plater::load_project(const wxString& filename)
{
if (filename.empty())
return;
p->reset();
p->set_project_filename(input_file);
p->set_project_filename(filename);
std::vector<fs::path> input_paths;
input_paths.push_back(into_path(input_file));
input_paths.push_back(into_path(filename));
load_files(input_paths);
}

View file

@ -138,6 +138,7 @@ public:
void new_project();
void load_project();
void load_project(const wxString& filename);
void add_model();
void extract_config_from_project();

View file

@ -461,6 +461,7 @@ const std::vector<std::string>& Preset::sla_print_options()
"support_pillar_widening_factor",
"support_base_diameter",
"support_base_height",
"support_base_safety_distance",
"support_critical_angle",
"support_max_bridge_length",
"support_max_pillar_link_distance",
@ -474,6 +475,10 @@ const std::vector<std::string>& Preset::sla_print_options()
"pad_max_merge_distance",
"pad_edge_radius",
"pad_wall_slope",
"pad_object_gap",
"pad_object_connector_stride",
"pad_object_connector_width",
"pad_object_connector_penetration",
"output_filename_format",
"default_sla_print_profile",
"compatible_printers",

View file

@ -3515,6 +3515,7 @@ void TabSLAPrint::build()
// 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_base_safety_distance");
optgroup->append_single_option_line("support_object_elevation");
optgroup = page->new_optgroup(_(L("Connection of the support sticks and junctions")));
@ -3535,7 +3536,12 @@ void TabSLAPrint::build()
// TODO: Disabling this parameter for the beta release
// optgroup->append_single_option_line("pad_edge_radius");
optgroup->append_single_option_line("pad_wall_slope");
optgroup->append_single_option_line("pad_object_gap");
optgroup->append_single_option_line("pad_object_connector_stride");
optgroup->append_single_option_line("pad_object_connector_width");
optgroup->append_single_option_line("pad_object_connector_penetration");
page = add_options_page(_(L("Advanced")), "wrench");
optgroup = page->new_optgroup(_(L("Slicing")));
optgroup->append_single_option_line("slice_closing_radius");
@ -3580,36 +3586,57 @@ void TabSLAPrint::update()
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));
}
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."));
load_config(new_conf);
}
auto dialog = new wxMessageDialog(parent(),
msg_text,
_(L("Invalid Head penetration")),
wxICON_WARNING | wxOK);
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));
}
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);
}
load_config(new_conf);
}
m_update_cnt--;
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."));
if (m_update_cnt == 0)
wxGetApp().mainframe->on_config_changed(m_config);
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);
}
// if(m_config->opt_float("support_object_elevation") < EPSILON &&
// m_config->opt_bool("pad_enable")) {
// // TODO: disable editding of:
// // pad_object_connector_stride
// // pad_object_connector_width
// // pad_object_connector_penetration
// }
m_update_cnt--;
if (m_update_cnt == 0) wxGetApp().mainframe->on_config_changed(m_config);
}
} // GUI