mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 06:15:58 +08:00
Merge branch 'master' into fs_dir_per_glyph
This commit is contained in:
commit
6751bba96e
@ -2186,11 +2186,11 @@ msgid "Filament"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -2361,11 +2361,11 @@ msgid "Filament"
|
||||
msgstr "Філамент"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -2404,11 +2404,11 @@ msgid "Filament"
|
||||
msgstr "Filament"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -2195,11 +2195,11 @@ msgid "Filament"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -2358,11 +2358,11 @@ msgid "Filament"
|
||||
msgstr "Filament"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -2258,11 +2258,11 @@ msgid "Filament"
|
||||
msgstr "필라멘트"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -2285,11 +2285,11 @@ msgid "Filament"
|
||||
msgstr "필라멘트 설정을 선택"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -2285,11 +2285,11 @@ msgid "Filament"
|
||||
msgstr "필라멘트 설정을 선택"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -2363,11 +2363,11 @@ msgid "Filament"
|
||||
msgstr "Filament"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -2361,11 +2361,11 @@ msgid "Filament"
|
||||
msgstr "Filamento"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -2396,11 +2396,11 @@ msgid "Filament"
|
||||
msgstr "Профиль прутка"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -2366,11 +2366,11 @@ msgid "Filament"
|
||||
msgstr "Filament"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -2311,11 +2311,11 @@ msgid "Filament"
|
||||
msgstr "Філамент"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -2275,11 +2275,11 @@ msgid "Filament"
|
||||
msgstr "耗材"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -2271,11 +2271,11 @@ msgid "Filament"
|
||||
msgstr "線材"
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Hide Custom GCode"
|
||||
msgid "Hide Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3954
|
||||
msgid "Show Custom GCode"
|
||||
msgid "Show Custom G-code"
|
||||
msgstr ""
|
||||
|
||||
#: src/slic3r/GUI/GCodeViewer.cpp:3967
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static constexpr const float NarrowInfillAreaThresholdMM = 3.f;
|
||||
//static constexpr const float NarrowInfillAreaThresholdMM = 3.f;
|
||||
|
||||
struct SurfaceFillParams
|
||||
{
|
||||
|
@ -106,15 +106,15 @@ ThickPolylines make_fill_polylines(
|
||||
coord_t length_filter = scale_(4);
|
||||
size_t skips_allowed = 2;
|
||||
size_t min_removal_conut = 5;
|
||||
for (int section_idx = 0; section_idx < polygon_sections.size(); section_idx++) {
|
||||
for (int line_idx = 0; line_idx < polygon_sections[section_idx].size(); line_idx++) {
|
||||
for (int section_idx = 0; section_idx < int(polygon_sections.size()); ++ section_idx) {
|
||||
for (int line_idx = 0; line_idx < int(polygon_sections[section_idx].size()); ++ line_idx) {
|
||||
if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) {
|
||||
std::set<std::pair<int, int>> to_remove{{section_idx, line_idx}};
|
||||
std::vector<Node> to_visit{{section_idx, line_idx}};
|
||||
|
||||
bool initial_touches_long_lines = false;
|
||||
if (section_idx > 0) {
|
||||
for (int prev_line_idx = 0; prev_line_idx < polygon_sections[section_idx - 1].size(); prev_line_idx++) {
|
||||
for (int prev_line_idx = 0; prev_line_idx < int(polygon_sections[section_idx - 1].size()); ++ prev_line_idx) {
|
||||
if (const Line &nl = polygon_sections[section_idx - 1][prev_line_idx];
|
||||
nl.a != nl.b && segments_overlap(line.a.y(), line.b.y(), nl.a.y(), nl.b.y())) {
|
||||
initial_touches_long_lines = true;
|
||||
@ -127,7 +127,7 @@ ThickPolylines make_fill_polylines(
|
||||
const Line &curr_l = polygon_sections[curr.section_idx][curr.line_idx];
|
||||
if (curr.neighbours_explored) {
|
||||
bool is_valid_for_removal = (curr_l.length() < length_filter) &&
|
||||
((int(to_remove.size()) - curr.skips_taken > min_removal_conut) ||
|
||||
((int(to_remove.size()) - curr.skips_taken > int(min_removal_conut)) ||
|
||||
(curr.neighbours.empty() && !initial_touches_long_lines));
|
||||
if (!is_valid_for_removal) {
|
||||
for (const auto &n : curr.neighbours) {
|
||||
@ -144,9 +144,9 @@ ThickPolylines make_fill_polylines(
|
||||
} else {
|
||||
to_visit.back().neighbours_explored = true;
|
||||
int curr_index = to_visit.size() - 1;
|
||||
bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < skips_allowed;
|
||||
if (curr.section_idx + 1 < polygon_sections.size()) {
|
||||
for (int lidx = 0; lidx < polygon_sections[curr.section_idx + 1].size(); lidx++) {
|
||||
bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < int(skips_allowed);
|
||||
if (curr.section_idx + 1 < int(polygon_sections.size())) {
|
||||
for (int lidx = 0; lidx < int(polygon_sections[curr.section_idx + 1].size()); ++ lidx) {
|
||||
if (const Line &nl = polygon_sections[curr.section_idx + 1][lidx];
|
||||
nl.a != nl.b && segments_overlap(curr_l.a.y(), curr_l.b.y(), nl.a.y(), nl.b.y()) &&
|
||||
(nl.length() < length_filter || can_use_skip)) {
|
||||
|
@ -3909,7 +3909,7 @@ void GCodeProcessor::post_process()
|
||||
std::stringstream ss(cmd.substr(1));
|
||||
int tool_number = -1;
|
||||
ss >> tool_number;
|
||||
if (tool_number != -1)
|
||||
if (tool_number != -1) {
|
||||
if (tool_number < 0 || (int)m_extruder_temps_config.size() <= tool_number) {
|
||||
// found an invalid value, clamp it to a valid one
|
||||
tool_number = std::clamp<int>(0, m_extruder_temps_config.size() - 1, tool_number);
|
||||
@ -3922,28 +3922,29 @@ void GCodeProcessor::post_process()
|
||||
if (m_print != nullptr)
|
||||
m_print->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning);
|
||||
}
|
||||
export_lines.insert_lines(backtrace, cmd,
|
||||
// line inserter
|
||||
[tool_number, this](unsigned int id, float time, float time_diff) {
|
||||
int temperature = int( m_layer_id != 1 ? m_extruder_temps_config[tool_number] : m_extruder_temps_first_layer_config[tool_number]);
|
||||
const std::string out = "M104 T" + std::to_string(tool_number) + " P" + std::to_string(int(std::round(time_diff))) + " S" + std::to_string(temperature) + "\n";
|
||||
return out;
|
||||
},
|
||||
// line replacer
|
||||
[this, tool_number](const std::string& line) {
|
||||
if (GCodeReader::GCodeLine::cmd_is(line, "M104")) {
|
||||
GCodeReader::GCodeLine gline;
|
||||
GCodeReader reader;
|
||||
reader.parse_line(line, [&gline](GCodeReader& reader, const GCodeReader::GCodeLine& l) { gline = l; });
|
||||
}
|
||||
export_lines.insert_lines(backtrace, cmd,
|
||||
// line inserter
|
||||
[tool_number, this](unsigned int id, float time, float time_diff) {
|
||||
int temperature = int( m_layer_id != 1 ? m_extruder_temps_config[tool_number] : m_extruder_temps_first_layer_config[tool_number]);
|
||||
const std::string out = "M104 T" + std::to_string(tool_number) + " P" + std::to_string(int(std::round(time_diff))) + " S" + std::to_string(temperature) + "\n";
|
||||
return out;
|
||||
},
|
||||
// line replacer
|
||||
[this, tool_number](const std::string& line) {
|
||||
if (GCodeReader::GCodeLine::cmd_is(line, "M104")) {
|
||||
GCodeReader::GCodeLine gline;
|
||||
GCodeReader reader;
|
||||
reader.parse_line(line, [&gline](GCodeReader& reader, const GCodeReader::GCodeLine& l) { gline = l; });
|
||||
|
||||
float val;
|
||||
if (gline.has_value('T', val) && gline.raw().find("cooldown") != std::string::npos && m_is_XL_printer) {
|
||||
if (static_cast<int>(val) == tool_number)
|
||||
return std::string("; removed M104\n");
|
||||
}
|
||||
float val;
|
||||
if (gline.has_value('T', val) && gline.raw().find("cooldown") != std::string::npos && m_is_XL_printer) {
|
||||
if (static_cast<int>(val) == tool_number)
|
||||
return std::string("; removed M104\n");
|
||||
}
|
||||
return line;
|
||||
});
|
||||
}
|
||||
return line;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1364,14 +1364,9 @@ std::vector<std::vector<float>> WipeTower::extract_wipe_volumes(const PrintConfi
|
||||
wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
|
||||
|
||||
// Also include filament_minimal_purge_on_wipe_tower. This is needed for the preview.
|
||||
for (unsigned int i = 0; i<number_of_extruders; ++i) {
|
||||
for (unsigned int j = 0; j<number_of_extruders; ++j) {
|
||||
float w = wipe_volumes[i][j];
|
||||
|
||||
if (wipe_volumes[i][j] < config.filament_minimal_purge_on_wipe_tower.get_at(j))
|
||||
wipe_volumes[i][j] = config.filament_minimal_purge_on_wipe_tower.get_at(j);
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 0; i<number_of_extruders; ++i)
|
||||
for (unsigned int j = 0; j<number_of_extruders; ++j)
|
||||
wipe_volumes[i][j] = std::max<float>(wipe_volumes[i][j], config.filament_minimal_purge_on_wipe_tower.get_at(j));
|
||||
|
||||
return wipe_volumes;
|
||||
}
|
||||
|
@ -84,16 +84,16 @@ void Layer::make_slices()
|
||||
co.MiterLimit = scaled<double>(3.);
|
||||
// Use the default zero edge merging distance. For this kind of safety offset the accuracy of normal direction is not important.
|
||||
// co.ShortestEdgeLength = delta * ClipperOffsetShortestEdgeFactor;
|
||||
static constexpr const double accept_area_threshold_ccw = sqr(scaled<double>(0.1 * delta));
|
||||
// static constexpr const double accept_area_threshold_ccw = sqr(scaled<double>(0.1 * delta));
|
||||
// Such a small hole should not survive the shrinkage, it should grow over
|
||||
static constexpr const double accept_area_threshold_cw = sqr(scaled<double>(0.2 * delta));
|
||||
// static constexpr const double accept_area_threshold_cw = sqr(scaled<double>(0.2 * delta));
|
||||
|
||||
for (const ExPolygon &expoly : expolygons) {
|
||||
contours.clear();
|
||||
co.Clear();
|
||||
co.AddPath(expoly.contour.points, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
|
||||
co.Execute(contours, - delta);
|
||||
size_t num_prev = out.size();
|
||||
// size_t num_prev = out.size();
|
||||
if (! contours.empty()) {
|
||||
holes.clear();
|
||||
for (const Polygon &hole : expoly.holes) {
|
||||
@ -447,7 +447,7 @@ static void connect_layer_slices(
|
||||
for (int i = int(other_layer.lslices_ex.size()) - 1; i >= 0; -- i)
|
||||
if (contour_aabb.overlap(other_layer.lslices_ex[i].bbox))
|
||||
// it is potentially slow, but should be executed rarely
|
||||
if (Polygons overlap = intersection(contour_poly, other_layer.lslices[i]); ! overlap.empty())
|
||||
if (Polygons overlap = intersection(contour_poly, other_layer.lslices[i]); ! overlap.empty()) {
|
||||
if (other_has_duplicates) {
|
||||
// Find the contour with the largest overlap. It is expected that the other overlap will be very small.
|
||||
double a = area(overlap);
|
||||
@ -460,6 +460,7 @@ static void connect_layer_slices(
|
||||
i_largest = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(i_largest >= 0);
|
||||
return i_largest;
|
||||
}
|
||||
@ -500,10 +501,10 @@ static void connect_layer_slices(
|
||||
#endif // NDEBUG
|
||||
|
||||
// Scatter the links, but don't sort them yet.
|
||||
for (int32_t islice = 0; islice < below.lslices_ex.size(); ++ islice)
|
||||
for (int32_t islice = 0; islice < int32_t(below.lslices_ex.size()); ++ islice)
|
||||
for (LayerSlice::Link &link : below.lslices_ex[islice].overlaps_above)
|
||||
above.lslices_ex[link.slice_idx].overlaps_below.push_back({ islice, link.area });
|
||||
for (int32_t islice = 0; islice < above.lslices_ex.size(); ++ islice)
|
||||
for (int32_t islice = 0; islice < int32_t(above.lslices_ex.size()); ++ islice)
|
||||
for (LayerSlice::Link &link : above.lslices_ex[islice].overlaps_below)
|
||||
below.lslices_ex[link.slice_idx].overlaps_above.push_back({ islice, link.area });
|
||||
// Sort the links.
|
||||
@ -935,7 +936,7 @@ void Layer::sort_perimeters_into_islands(
|
||||
island.fill_region_id = LayerIsland::fill_region_composite_id;
|
||||
for (uint32_t fill_idx : fill_range) {
|
||||
if (const int fill_regon_id = map_expolygon_to_region_and_fill[fill_idx].region_id;
|
||||
fill_regon_id == -1 || (island.fill_region_id != LayerIsland::fill_region_composite_id && island.fill_region_id != fill_regon_id)) {
|
||||
fill_regon_id == -1 || (island.fill_region_id != LayerIsland::fill_region_composite_id && int(island.fill_region_id) != fill_regon_id)) {
|
||||
island.fill_region_id = LayerIsland::fill_region_composite_id;
|
||||
break;
|
||||
} else
|
||||
|
@ -376,7 +376,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||
|
||||
// Expand the top / bottom / bridge surfaces into the shell thickness solid infills.
|
||||
double layer_thickness;
|
||||
ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(m_fill_surfaces.surfaces, {stInternalSolid, stTop}, layer_thickness));
|
||||
ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(m_fill_surfaces.surfaces, {stInternalSolid}, layer_thickness));
|
||||
|
||||
SurfaceCollection bridges;
|
||||
{
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/iostream.hpp>
|
||||
|
||||
#include <tbb/concurrent_vector.h>
|
||||
|
||||
#include "SVG.hpp"
|
||||
#include <Eigen/Dense>
|
||||
#include "GCodeWriter.hpp"
|
||||
@ -1055,12 +1057,18 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
|
||||
// This method is used by the auto arrange function.
|
||||
Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const
|
||||
{
|
||||
Points pts;
|
||||
for (const ModelVolume* v : volumes) {
|
||||
if (v->is_model_part())
|
||||
append(pts, its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast<float>(), 0.0f).points);
|
||||
}
|
||||
return Geometry::convex_hull(std::move(pts));
|
||||
tbb::concurrent_vector<Polygon> chs;
|
||||
chs.reserve(volumes.size());
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, volumes.size()), [&](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t i = range.begin(); i < range.end(); ++i) {
|
||||
const ModelVolume* v = volumes[i];
|
||||
chs.emplace_back(its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast<float>(), 0.0f));
|
||||
}
|
||||
});
|
||||
|
||||
Polygons polygons;
|
||||
polygons.assign(chs.begin(), chs.end());
|
||||
return Geometry::convex_hull(polygons);
|
||||
}
|
||||
|
||||
void ModelObject::center_around_origin(bool include_modifiers)
|
||||
|
@ -188,7 +188,7 @@ public:
|
||||
void assign(const LayerHeightProfile &rhs) { if (! this->timestamp_matches(rhs)) { m_data = rhs.m_data; this->copy_timestamp(rhs); } }
|
||||
void assign(LayerHeightProfile &&rhs) { if (! this->timestamp_matches(rhs)) { m_data = std::move(rhs.m_data); this->copy_timestamp(rhs); } }
|
||||
|
||||
std::vector<coordf_t> get() const throw() { return m_data; }
|
||||
const std::vector<coordf_t>& get() const throw() { return m_data; }
|
||||
bool empty() const throw() { return m_data.empty(); }
|
||||
void set(const std::vector<coordf_t> &data) { if (m_data != data) { m_data = data; this->touch(); } }
|
||||
void set(std::vector<coordf_t> &&data) { if (m_data != data) { m_data = std::move(data); this->touch(); } }
|
||||
|
@ -582,7 +582,7 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P
|
||||
}
|
||||
|
||||
// Prefer non-overhang point as a starting point.
|
||||
for (const std::pair<Point, PointInfo> pt : point_occurrence)
|
||||
for (const std::pair<Point, PointInfo> &pt : point_occurrence)
|
||||
if (pt.second.occurrence == 1) {
|
||||
start_point = pt.first;
|
||||
if (!pt.second.is_overhang) {
|
||||
@ -743,7 +743,7 @@ ExtrusionPaths sort_extra_perimeters(const ExtrusionPaths& extra_perims, int ind
|
||||
}
|
||||
|
||||
std::vector<bool> processed(extra_perims.size(), false);
|
||||
for (size_t path_idx = 0; path_idx < index_of_first_unanchored; path_idx++) {
|
||||
for (int path_idx = 0; path_idx < index_of_first_unanchored; path_idx++) {
|
||||
processed[path_idx] = true;
|
||||
}
|
||||
|
||||
|
@ -301,7 +301,7 @@ void ThickPolyline::start_at_index(int index)
|
||||
{
|
||||
assert(index >= 0 && index < this->points.size());
|
||||
assert(this->points.front() == this->points.back() && this->width.front() == this->width.back());
|
||||
if (index != 0 && index != (this->points.size() - 1) && this->points.front() == this->points.back() && this->width.front() == this->width.back()) {
|
||||
if (index != 0 && index + 1 != int(this->points.size()) && this->points.front() == this->points.back() && this->width.front() == this->width.back()) {
|
||||
this->points.pop_back();
|
||||
assert(this->points.size() * 2 == this->width.size());
|
||||
std::rotate(this->points.begin(), this->points.begin() + index, this->points.end());
|
||||
|
@ -469,11 +469,13 @@ std::string Print::validate(std::string* warning) const
|
||||
return _u8L("The supplied settings will cause an empty print.");
|
||||
|
||||
if (m_config.complete_objects) {
|
||||
if (! sequential_print_horizontal_clearance_valid(*this))
|
||||
if (!sequential_print_horizontal_clearance_valid(*this, const_cast<Polygons*>(&m_sequential_print_clearance_contours)))
|
||||
return _u8L("Some objects are too close; your extruder will collide with them.");
|
||||
if (! sequential_print_vertical_clearance_valid(*this))
|
||||
return _u8L("Some objects are too tall and cannot be printed without extruder collisions.");
|
||||
if (!sequential_print_vertical_clearance_valid(*this))
|
||||
return _u8L("Some objects are too tall and cannot be printed without extruder collisions.");
|
||||
}
|
||||
else
|
||||
const_cast<Polygons*>(&m_sequential_print_clearance_contours)->clear();
|
||||
|
||||
if (m_config.avoid_crossing_perimeters && m_config.avoid_crossing_curled_overhangs) {
|
||||
return _u8L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together.");
|
||||
@ -1227,7 +1229,7 @@ void Print::alert_when_supports_needed()
|
||||
}
|
||||
|
||||
std::string translated_list = expansion_rule;
|
||||
for (int i = 0; i < translated_elements.size() - 1; i++) {
|
||||
for (int i = 0; i < int(translated_elements.size()) - 1; ++ i) {
|
||||
auto first_elem = translated_list.find("%1%");
|
||||
assert(first_elem != translated_list.npos);
|
||||
translated_list.replace(first_elem, 3, translated_elements[i]);
|
||||
@ -1235,7 +1237,7 @@ void Print::alert_when_supports_needed()
|
||||
// expand the translated list by another application of the same rule
|
||||
auto second_elem = translated_list.find("%2%");
|
||||
assert(second_elem != translated_list.npos);
|
||||
if (i < translated_elements.size() - 2) {
|
||||
if (i < int(translated_elements.size()) - 2) {
|
||||
translated_list.replace(second_elem, 3, expansion_rule);
|
||||
} else {
|
||||
translated_list.replace(second_elem, 3, translated_elements[i + 1]);
|
||||
|
@ -609,6 +609,7 @@ public:
|
||||
const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; }
|
||||
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; }
|
||||
|
||||
const Polygons& get_sequential_print_clearance_contours() const { return m_sequential_print_clearance_contours; }
|
||||
static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr);
|
||||
|
||||
protected:
|
||||
@ -658,6 +659,9 @@ private:
|
||||
// Estimated print time, filament consumed.
|
||||
PrintStatistics m_print_statistics;
|
||||
|
||||
// Cache to store sequential print clearance contours
|
||||
Polygons m_sequential_print_clearance_contours;
|
||||
|
||||
// To allow GCode to set the Print's GCodeExport step status.
|
||||
friend class GCode;
|
||||
// To allow GCodeProcessor to emit warnings.
|
||||
|
@ -1678,7 +1678,7 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
}
|
||||
}
|
||||
unsupported_area = closing(unsupported_area, SCALED_EPSILON);
|
||||
unsupported_area = closing(unsupported_area, float(SCALED_EPSILON));
|
||||
// By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids
|
||||
// NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole
|
||||
lower_layer_solids = shrink(lower_layer_solids, 1 * spacing); // first remove thin regions that will not support anything
|
||||
@ -1703,7 +1703,7 @@ void PrintObject::bridge_over_infill()
|
||||
worth_bridging.push_back(p);
|
||||
}
|
||||
}
|
||||
worth_bridging = intersection(closing(worth_bridging, SCALED_EPSILON), s->expolygon);
|
||||
worth_bridging = intersection(closing(worth_bridging, float(SCALED_EPSILON)), s->expolygon);
|
||||
candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0));
|
||||
|
||||
#ifdef DEBUG_BRIDGE_OVER_INFILL
|
||||
@ -1860,7 +1860,7 @@ void PrintObject::bridge_over_infill()
|
||||
|
||||
// cluster layers by depth needed for thick bridges. Each cluster is to be processed by single thread sequentially, so that bridges cannot appear one on another
|
||||
std::vector<std::vector<size_t>> clustered_layers_for_threads;
|
||||
float target_flow_height_factor = 0.9;
|
||||
float target_flow_height_factor = 0.9f;
|
||||
{
|
||||
std::vector<size_t> layers_with_candidates;
|
||||
std::map<size_t, Polygons> layer_area_covered_by_candidates;
|
||||
@ -1937,9 +1937,9 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
}
|
||||
layers_sparse_infill = union_ex(layers_sparse_infill);
|
||||
layers_sparse_infill = closing_ex(layers_sparse_infill, SCALED_EPSILON);
|
||||
layers_sparse_infill = closing_ex(layers_sparse_infill, float(SCALED_EPSILON));
|
||||
not_sparse_infill = union_ex(not_sparse_infill);
|
||||
not_sparse_infill = closing_ex(not_sparse_infill, SCALED_EPSILON);
|
||||
not_sparse_infill = closing_ex(not_sparse_infill, float(SCALED_EPSILON));
|
||||
return diff(layers_sparse_infill, not_sparse_infill);
|
||||
};
|
||||
|
||||
@ -2276,8 +2276,8 @@ void PrintObject::bridge_over_infill()
|
||||
lightning_area.insert(lightning_area.end(), l.begin(), l.end());
|
||||
}
|
||||
}
|
||||
total_fill_area = closing(total_fill_area, SCALED_EPSILON);
|
||||
expansion_area = closing(expansion_area, SCALED_EPSILON);
|
||||
total_fill_area = closing(total_fill_area, float(SCALED_EPSILON));
|
||||
expansion_area = closing(expansion_area, float(SCALED_EPSILON));
|
||||
expansion_area = intersection(expansion_area, deep_infill_area);
|
||||
Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing));
|
||||
Polygons internal_unsupported_area = shrink(deep_infill_area, spacing * 4.5);
|
||||
@ -2595,7 +2595,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
|
||||
|
||||
if (layer_height_profile.empty()) {
|
||||
// use the constructor because the assignement is crashing on ASAN OsX
|
||||
layer_height_profile = std::vector<coordf_t>(model_object.layer_height_profile.get());
|
||||
layer_height_profile = model_object.layer_height_profile.get();
|
||||
// layer_height_profile = model_object.layer_height_profile;
|
||||
// The layer height returned is sampled with high density for the UI layer height painting
|
||||
// and smoothing tool to work.
|
||||
|
@ -186,7 +186,7 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
|
||||
auto last_z = [&layer_height_profile]() {
|
||||
return layer_height_profile.empty() ? 0. : *(layer_height_profile.end() - 2);
|
||||
};
|
||||
auto lh_append = [&layer_height_profile, last_z](coordf_t z, coordf_t layer_height) {
|
||||
auto lh_append = [&layer_height_profile](coordf_t z, coordf_t layer_height) {
|
||||
if (! layer_height_profile.empty()) {
|
||||
bool last_z_matches = is_approx(*(layer_height_profile.end() - 2), z);
|
||||
bool last_h_matches = is_approx(layer_height_profile.back(), layer_height);
|
||||
|
@ -26,6 +26,433 @@ namespace Slic3r
|
||||
namespace FFFTreeSupport
|
||||
{
|
||||
|
||||
// Single slice through a single branch or trough a number of branches.
|
||||
struct Slice
|
||||
{
|
||||
// All polygons collected for this slice.
|
||||
Polygons polygons;
|
||||
// All bottom contacts collected for this slice.
|
||||
Polygons bottom_contacts;
|
||||
// How many branches were merged in this slice? Used to decide whether ClipperLib union is needed.
|
||||
size_t num_branches{ 0 };
|
||||
};
|
||||
|
||||
struct Element
|
||||
{
|
||||
// Current position of the centerline including the Z coordinate, unscaled.
|
||||
Vec3f position;
|
||||
float radius;
|
||||
|
||||
// Index of this layer, including the raft layers.
|
||||
LayerIndex layer_idx;
|
||||
|
||||
// Limits where the centerline could be placed at the current layer Z.
|
||||
Polygons influence_area;
|
||||
|
||||
// Locked node should not be moved. Locked nodes are at the top of an object or at the tips of branches.
|
||||
bool locked;
|
||||
|
||||
// Previous position, for Laplacian smoothing, unscaled.
|
||||
Vec3f prev_position;
|
||||
|
||||
// For sphere tracing and other collision detection optimizations.
|
||||
Vec3f last_collision;
|
||||
double last_collision_depth;
|
||||
|
||||
struct CollisionSphere {
|
||||
// Minimum Z for which the sphere collision will be evaluated.
|
||||
// Limited by the minimum sloping angle and by the bottom of the tree.
|
||||
float min_z{ -std::numeric_limits<float>::max() };
|
||||
// Maximum Z for which the sphere collision will be evaluated.
|
||||
// Limited by the minimum sloping angle and by the tip of the current branch.
|
||||
float max_z{ std::numeric_limits<float>::max() };
|
||||
// Span of layers to test collision of this sphere against.
|
||||
uint32_t layer_begin;
|
||||
uint32_t layer_end;
|
||||
};
|
||||
|
||||
CollisionSphere collision_sphere;
|
||||
};
|
||||
|
||||
struct Branch;
|
||||
|
||||
struct Bifurcation
|
||||
{
|
||||
Branch *branch;
|
||||
double area;
|
||||
};
|
||||
|
||||
// Single branch of a tree.
|
||||
struct Branch
|
||||
{
|
||||
std::vector<Element> path;
|
||||
|
||||
using Bifurcations =
|
||||
#ifdef NDEBUG
|
||||
// To reduce memory allocation in release mode.
|
||||
boost::container::small_vector<Bifurcation, 4>;
|
||||
#else // NDEBUG
|
||||
// To ease debugging.
|
||||
std::vector<Bifurcation>;
|
||||
#endif // NDEBUG
|
||||
|
||||
Bifurcations up;
|
||||
Bifurcation down;
|
||||
|
||||
// How many of the thick up branches are considered continuation of the trunk?
|
||||
// These will be smoothed out together.
|
||||
size_t num_up_trunk;
|
||||
|
||||
bool has_root() const { return this->down.branch == nullptr; }
|
||||
bool has_tip() const { return this->up.empty(); }
|
||||
};
|
||||
|
||||
struct Tree
|
||||
{
|
||||
// Branches: Store of all branches.
|
||||
// The first branch is the root of the tree.
|
||||
Slic3r::deque<Branch> branches;
|
||||
|
||||
Branch& root() { return branches.front(); }
|
||||
const Branch& root() const { return branches.front(); }
|
||||
|
||||
// Result of slicing the branches.
|
||||
std::vector<Slice> slices;
|
||||
// First layer index of the first slice in the vector above.
|
||||
LayerIndex first_layer_id{ -1 };
|
||||
};
|
||||
|
||||
using Forest = std::vector<Tree>;
|
||||
using Trees = std::vector<Tree>;
|
||||
|
||||
Element to_tree_element(const TreeSupportSettings &config, const SlicingParameters &slicing_params, SupportElement &element, bool is_root)
|
||||
{
|
||||
Element out;
|
||||
out.position = to_3d(unscaled<float>(element.state.result_on_layer), float(layer_z(slicing_params, config, element.state.layer_idx)));
|
||||
out.radius = support_element_radius(config, element);
|
||||
out.layer_idx = element.state.layer_idx;
|
||||
out.influence_area = std::move(element.influence_area);
|
||||
out.locked = (is_root && element.state.layer_idx > 0) || element.state.locked();
|
||||
return out;
|
||||
}
|
||||
|
||||
// Convert move bounds into a forest of trees, each tree made of a graph of branches and bifurcation points.
|
||||
// Destroys move_bounds.
|
||||
Forest make_forest(const TreeSupportSettings &config, const SlicingParameters &slicing_params, std::vector<SupportElements> &&move_bounds)
|
||||
{
|
||||
struct TreeVisitor {
|
||||
void visit_recursive(std::vector<SupportElements> &move_bounds, SupportElement &start_element, Branch *parent_branch, Tree &out) const {
|
||||
assert(! start_element.state.marked && ! start_element.parents.empty());
|
||||
// Collect elements up to a bifurcation above.
|
||||
start_element.state.marked = true;
|
||||
// For each branch bifurcating from this point:
|
||||
// SupportElements &layer = move_bounds[start_element.state.layer_idx];
|
||||
SupportElements &layer_above = move_bounds[start_element.state.layer_idx + 1];
|
||||
for (size_t parent_idx = 0; parent_idx < start_element.parents.size(); ++ parent_idx) {
|
||||
Branch branch;
|
||||
if (parent_branch)
|
||||
// Duplicate the last element of the trunk below.
|
||||
// If this branch has a smaller diameter than the trunk below, its centerline will not be aligned with the centerline of the trunk.
|
||||
branch.path.emplace_back(parent_branch->path.back());
|
||||
branch.path.emplace_back(to_tree_element(config, slicing_params, start_element, parent_branch == nullptr));
|
||||
// Traverse each branch until it branches again.
|
||||
SupportElement &first_parent = layer_above[start_element.parents[parent_idx]];
|
||||
assert(! first_parent.state.marked);
|
||||
assert(branch.path.back().layer_idx + 1 == first_parent.state.layer_idx);
|
||||
branch.path.emplace_back(to_tree_element(config, slicing_params, first_parent, false));
|
||||
if (first_parent.parents.size() < 2)
|
||||
first_parent.state.marked = true;
|
||||
SupportElement *next_branch = nullptr;
|
||||
if (first_parent.parents.size() == 1) {
|
||||
for (SupportElement *parent = &first_parent;;) {
|
||||
assert(parent->state.marked);
|
||||
SupportElement &next_parent = move_bounds[parent->state.layer_idx + 1][parent->parents.front()];
|
||||
assert(! next_parent.state.marked);
|
||||
assert(branch.path.back().layer_idx + 1 == next_parent.state.layer_idx);
|
||||
branch.path.emplace_back(to_tree_element(config, slicing_params, next_parent, false));
|
||||
if (next_parent.parents.size() > 1) {
|
||||
// Branching point was reached.
|
||||
next_branch = &next_parent;
|
||||
break;
|
||||
}
|
||||
next_parent.state.marked = true;
|
||||
if (next_parent.parents.size() == 0)
|
||||
// Tip is reached.
|
||||
break;
|
||||
parent = &next_parent;
|
||||
}
|
||||
} else if (first_parent.parents.size() > 1)
|
||||
// Branching point was reached.
|
||||
next_branch = &first_parent;
|
||||
assert(branch.path.size() >= 2);
|
||||
assert(next_branch == nullptr || ! next_branch->state.marked);
|
||||
out.branches.emplace_back(std::move(branch));
|
||||
Branch *pbranch = &out.branches.back();
|
||||
if (parent_branch) {
|
||||
parent_branch->up.push_back({ pbranch });
|
||||
pbranch->down = { parent_branch };
|
||||
}
|
||||
if (next_branch)
|
||||
this->visit_recursive(move_bounds, *next_branch, pbranch, out);
|
||||
}
|
||||
|
||||
if (parent_branch) {
|
||||
// Update initial radii of thin branches merging with a trunk.
|
||||
auto it_up_max_r = std::max_element(parent_branch->up.begin(), parent_branch->up.end(),
|
||||
[](const Bifurcation &l, const Bifurcation &r){ return l.branch->path[1].radius < r.branch->path[1].radius; });
|
||||
const float r1 = it_up_max_r->branch->path[1].radius;
|
||||
const float radius_increment = unscaled<float>(config.branch_radius_increase_per_layer);
|
||||
for (auto it = parent_branch->up.begin(); it != parent_branch->up.end(); ++ it)
|
||||
if (it != it_up_max_r) {
|
||||
Element &el = it->branch->path.front();
|
||||
Element &el2 = it->branch->path[1];
|
||||
if (! is_approx(r1, el2.radius))
|
||||
el.radius = std::min(el.radius, el2.radius + radius_increment);
|
||||
}
|
||||
// Sort children of parent_branch by decreasing radius.
|
||||
std::sort(parent_branch->up.begin(), parent_branch->up.end(),
|
||||
[](const Bifurcation &l, const Bifurcation &r){ return l.branch->path.front().radius > r.branch->path.front().radius; });
|
||||
// Update number of branches to be considered a continuation of the trunk during smoothing.
|
||||
{
|
||||
const float r_trunk = 0.75 * it_up_max_r->branch->path.front().radius;
|
||||
parent_branch->num_up_trunk = 0;
|
||||
for (const Bifurcation& up : parent_branch->up)
|
||||
if (up.branch->path.front().radius < r_trunk)
|
||||
break;
|
||||
else
|
||||
++ parent_branch->num_up_trunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const TreeSupportSettings &config;
|
||||
const SlicingParameters &slicing_params;
|
||||
};
|
||||
|
||||
TreeVisitor visitor{ config, slicing_params };
|
||||
|
||||
for (SupportElements &elements : move_bounds)
|
||||
for (SupportElement &el : elements)
|
||||
el.state.marked = false;
|
||||
|
||||
Trees trees;
|
||||
for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
|
||||
for (SupportElement &start_element : move_bounds[layer_idx]) {
|
||||
if (! start_element.state.marked && ! start_element.parents.empty()) {
|
||||
#if 0
|
||||
{
|
||||
// Verify that this node is a root, such that there is no element in the layer below
|
||||
// that points to it.
|
||||
int ielement = &start_element - move_bounds.data();
|
||||
int found = 0;
|
||||
if (layer_idx > 0) {
|
||||
for (auto &el : move_bounds[layer_idx - 1]) {
|
||||
for (auto iparent : el.parents)
|
||||
if (iparent == ielement)
|
||||
++ found;
|
||||
}
|
||||
if (found != 0)
|
||||
printf("Found: %d\n", found);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
trees.push_back({});
|
||||
visitor.visit_recursive(move_bounds, start_element, nullptr, trees.back());
|
||||
assert(! trees.back().branches.empty());
|
||||
assert(! trees.back().branches.front().path.empty());
|
||||
#if 0
|
||||
// Debugging: Only build trees with specific properties.
|
||||
if (start_element.state.lost) {
|
||||
}
|
||||
else if (start_element.state.verylost) {
|
||||
}
|
||||
else
|
||||
trees.pop_back();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 1
|
||||
move_bounds.clear();
|
||||
#else
|
||||
for (SupportElements &elements : move_bounds)
|
||||
for (SupportElement &el : elements)
|
||||
el.state.marked = false;
|
||||
#endif
|
||||
|
||||
return trees;
|
||||
}
|
||||
|
||||
// Move bounds were propagated top to bottom. At each joint of branches the move bounds were reduced significantly.
|
||||
// Now reflect the reduction of tree space by propagating the reduction of tree centerline space
|
||||
// bottom-up starting with the bottom-most joint.
|
||||
void trim_influence_areas_bottom_up(Forest &forest, const float dxy_dlayer)
|
||||
{
|
||||
struct Trimmer {
|
||||
static void trim_recursive(Branch &branch, const float delta_r, const float dxy_dlayer) {
|
||||
assert(delta_r >= 0);
|
||||
if (delta_r > 0)
|
||||
branch.path.front().influence_area = offset(branch.path.front().influence_area, delta_r);
|
||||
for (size_t i = 1; i < branch.path.size(); ++ i)
|
||||
branch.path[i].influence_area = intersection(branch.path[i].influence_area, offset(branch.path[i - 1].influence_area, dxy_dlayer));
|
||||
const float r0 = branch.path.back().radius;
|
||||
for (Bifurcation &up : branch.up) {
|
||||
up.branch->path.front().influence_area = branch.path.back().influence_area;
|
||||
trim_recursive(*up.branch, r0 - up.branch->path.front().radius, dxy_dlayer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (Tree &tree : forest) {
|
||||
Branch &root = tree.root();
|
||||
const float r0 = root.path.back().radius;
|
||||
for (Bifurcation &up : root.up)
|
||||
Trimmer::trim_recursive(*up.branch, r0 - up.branch->path.front().radius, dxy_dlayer);
|
||||
}
|
||||
}
|
||||
|
||||
// Straighten up and smooth centerlines inside their influence areas.
|
||||
void smooth_trees_inside_influence_areas(Branch &root, bool is_root)
|
||||
{
|
||||
// Smooth the subtree:
|
||||
//
|
||||
// Apply laplacian and bilaplacian smoothing inside a branch,
|
||||
// apply laplacian smoothing only at a bifurcation point.
|
||||
//
|
||||
// Applying a bilaplacian smoothing inside a branch should ensure curvature of the brach to be lower
|
||||
// than the radius at each particular point of the centerline,
|
||||
// while omitting bilaplacian smoothing at bifurcation points will create sharp bifurcations.
|
||||
// Sharp bifurcations have a smaller volume, but just a tiny bit larger surfaces than smooth bifurcations
|
||||
// where each continuation of the trunk satifies the path radius > centerline element radius.
|
||||
const size_t num_iterations = 100;
|
||||
struct StackElement {
|
||||
Branch &branch;
|
||||
size_t idx_up;
|
||||
};
|
||||
std::vector<StackElement> stack;
|
||||
|
||||
auto adjust_position = [](Element &el, Vec2f new_pos) {
|
||||
Point new_pos_scaled = scaled<coord_t>(new_pos);
|
||||
if (! contains(el.influence_area, new_pos_scaled)) {
|
||||
int64_t min_dist = std::numeric_limits<int64_t>::max();
|
||||
Point min_proj_scaled;
|
||||
for (const Polygon& polygon : el.influence_area) {
|
||||
Point proj_scaled = polygon.point_projection(new_pos_scaled);
|
||||
if (int64_t dist = (proj_scaled - new_pos_scaled).cast<int64_t>().squaredNorm(); dist < min_dist) {
|
||||
min_dist = dist;
|
||||
min_proj_scaled = proj_scaled;
|
||||
}
|
||||
}
|
||||
new_pos = unscaled<float>(min_proj_scaled);
|
||||
}
|
||||
el.position.head<2>() = new_pos;
|
||||
};
|
||||
|
||||
for (size_t iter = 0; iter < num_iterations; ++ iter) {
|
||||
// 1) Back-up the current positions.
|
||||
stack.push_back({ root, 0 });
|
||||
while (! stack.empty()) {
|
||||
StackElement &state = stack.back();
|
||||
if (state.idx_up == state.branch.num_up_trunk) {
|
||||
// Process this path.
|
||||
for (auto &el : state.branch.path)
|
||||
el.prev_position = el.position;
|
||||
stack.pop_back();
|
||||
} else {
|
||||
// Open another up node of this branch.
|
||||
stack.push_back({ *state.branch.up[state.idx_up].branch, 0 });
|
||||
++ state.idx_up;
|
||||
}
|
||||
}
|
||||
// 2) Calculate new position.
|
||||
stack.push_back({ root, 0 });
|
||||
while (! stack.empty()) {
|
||||
StackElement &state = stack.back();
|
||||
if (state.idx_up == state.branch.num_up_trunk) {
|
||||
// Process this path.
|
||||
for (size_t i = 1; i + 1 < state.branch.path.size(); ++ i)
|
||||
if (auto &el = state.branch.path[i]; ! el.locked) {
|
||||
// Laplacian smoothing with 0.5 weight.
|
||||
const Vec3f &p0 = state.branch.path[i - 1].prev_position;
|
||||
const Vec3f &p1 = el.prev_position;
|
||||
const Vec3f &p2 = state.branch.path[i + 1].prev_position;
|
||||
adjust_position(el, 0.5 * p1.head<2>() + 0.25 * (p0.head<2>() + p2.head<2>()));
|
||||
#if 0
|
||||
// Only apply bilaplacian smoothing if the current curvature is smaller than el.radius.
|
||||
// Interpolate p0, p1, p2 with a circle.
|
||||
// First project p0, p1, p2 into a common plane.
|
||||
const Vec3f n = (p1 - p0).cross(p2 - p1);
|
||||
const Vec3f y = Vec3f(n.y(), n.x(), 0).normalized();
|
||||
const Vec2f q0{ p0.z(), p0.dot(y) };
|
||||
const Vec2f q1{ p1.z(), p1.dot(y) };
|
||||
const Vec2f q2{ p2.z(), p2.dot(y) };
|
||||
// Interpolate q0, q1, q2 with a circle, calculate its radius.
|
||||
Vec2f b = q1 - q0;
|
||||
Vec2f c = q2 - q0;
|
||||
float lb = b.squaredNorm();
|
||||
float lc = c.squaredNorm();
|
||||
if (float d = b.x() * c.y() - b.y() * c.x(); std::abs(d) > EPSILON) {
|
||||
Vec2f v = lc * b - lb * c;
|
||||
float r2 = 0.25f * v.squaredNorm() / sqr(d);
|
||||
if (r2 )
|
||||
}
|
||||
#endif
|
||||
}
|
||||
{
|
||||
// Laplacian smoothing with 0.5 weight, branching point.
|
||||
float weight = 0;
|
||||
Vec2f new_pos = Vec2f::Zero();
|
||||
for (size_t i = 0; i < state.branch.num_up_trunk; ++i) {
|
||||
const Element &el = state.branch.up[i].branch->path.front();
|
||||
new_pos += el.prev_position.head<2>();
|
||||
weight += el.radius;
|
||||
}
|
||||
{
|
||||
const Element &el = state.branch.path[state.branch.path.size() - 2];
|
||||
new_pos += el.prev_position.head<2>();
|
||||
weight *= 2.f;
|
||||
}
|
||||
adjust_position(state.branch.path.back(), 0.5f * state.branch.path.back().prev_position.head<2>() + 0.5f * weight * new_pos);
|
||||
}
|
||||
stack.pop_back();
|
||||
} else {
|
||||
// Open another up node of this branch.
|
||||
stack.push_back({ *state.branch.up[state.idx_up].branch, 0 });
|
||||
++ state.idx_up;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Also smoothen start of the path.
|
||||
if (Element &first = root.path.front(); ! first.locked) {
|
||||
Element &second = root.path[1];
|
||||
Vec2f new_pos = 0.75f * first.prev_position.head<2>() + 0.25f * second.prev_position.head<2>();
|
||||
if (is_root)
|
||||
// Let the root of the tree float inside its influence area.
|
||||
adjust_position(first, new_pos);
|
||||
else {
|
||||
// Keep the start of a thin branch inside the trunk.
|
||||
const Element &trunk = root.down.branch->path.back();
|
||||
const float rdif = trunk.radius - root.path.front().radius;
|
||||
assert(rdif >= 0);
|
||||
Vec2f vdif = new_pos - trunk.prev_position.head<2>();
|
||||
float ldif = vdif.squaredNorm();
|
||||
if (ldif > sqr(rdif))
|
||||
// Clamp new position.
|
||||
new_pos = trunk.prev_position.head<2>() + vdif * rdif / sqrt(ldif);
|
||||
first.position.head<2>() = new_pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void smooth_trees_inside_influence_areas(Forest &forest)
|
||||
{
|
||||
// Parallel for!
|
||||
for (Tree &tree : forest)
|
||||
smooth_trees_inside_influence_areas(tree.root(), true);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Test whether two circles, each on its own plane in 3D intersect.
|
||||
// Circles are considered intersecting, if the lowest point on one circle is below the other circle's plane.
|
||||
// Assumption: The two planes are oriented the same way.
|
||||
@ -46,6 +473,7 @@ static bool circles_intersect(
|
||||
assert(n1.dot(p2) >= n1.dot(lowest_point2));
|
||||
return n1.dot(lowest_point2) <= 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<bool flip_normals>
|
||||
void triangulate_fan(indexed_triangle_set &its, int ifan, int ibegin, int iend)
|
||||
@ -174,7 +602,8 @@ static std::pair<float, float> extrude_branch(
|
||||
// char fname[2048];
|
||||
// static int irun = 0;
|
||||
|
||||
float zmin, zmax;
|
||||
float zmin = 0;
|
||||
float zmax = 0;
|
||||
|
||||
for (size_t ipath = 1; ipath < path.size(); ++ ipath) {
|
||||
const SupportElement &prev = *path[ipath - 1];
|
||||
@ -390,7 +819,7 @@ static void organic_smooth_branches_avoid_collisions(
|
||||
collision_sphere.prev_position = collision_sphere.position;
|
||||
std::atomic<size_t> num_moved{ 0 };
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, collision_spheres.size()),
|
||||
[&collision_spheres, &layer_collision_cache, &slicing_params, &config, &move_bounds, &linear_data_layers, &num_moved, &throw_on_cancel](const tbb::blocked_range<size_t> range) {
|
||||
[&collision_spheres, &layer_collision_cache, &slicing_params, &config, &linear_data_layers, &num_moved, &throw_on_cancel](const tbb::blocked_range<size_t> range) {
|
||||
for (size_t collision_sphere_id = range.begin(); collision_sphere_id < range.end(); ++ collision_sphere_id)
|
||||
if (CollisionSphere &collision_sphere = collision_spheres[collision_sphere_id]; ! collision_sphere.locked) {
|
||||
// Calculate collision of multiple 2D layers against a collision sphere.
|
||||
@ -426,7 +855,7 @@ static void organic_smooth_branches_avoid_collisions(
|
||||
}
|
||||
// Laplacian smoothing
|
||||
Vec2d avg{ 0, 0 };
|
||||
const SupportElements &above = move_bounds[collision_sphere.element.state.layer_idx + 1];
|
||||
//const SupportElements &above = move_bounds[collision_sphere.element.state.layer_idx + 1];
|
||||
const size_t offset_above = linear_data_layers[collision_sphere.element.state.layer_idx + 1];
|
||||
double weight = 0.;
|
||||
for (auto iparent : collision_sphere.element.parents) {
|
||||
@ -599,7 +1028,7 @@ void organic_draw_branches(
|
||||
std::vector<std::pair<SupportElement*, int>> map_downwards_new;
|
||||
linear_data_layers.emplace_back(0);
|
||||
for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
|
||||
SupportElements *layer_above = layer_idx + 1 < move_bounds.size() ? &move_bounds[layer_idx + 1] : nullptr;
|
||||
SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
|
||||
map_downwards_new.clear();
|
||||
std::sort(map_downwards_old.begin(), map_downwards_old.end(), [](auto& l, auto& r) { return l.first < r.first; });
|
||||
SupportElements &layer = move_bounds[layer_idx];
|
||||
@ -675,7 +1104,7 @@ void organic_draw_branches(
|
||||
// Collect elements up to a bifurcation above.
|
||||
start_element.state.marked = true;
|
||||
// For each branch bifurcating from this point:
|
||||
SupportElements &layer = move_bounds[start_element.state.layer_idx];
|
||||
//SupportElements &layer = move_bounds[start_element.state.layer_idx];
|
||||
SupportElements &layer_above = move_bounds[start_element.state.layer_idx + 1];
|
||||
bool root = out.branches.empty();
|
||||
for (size_t parent_idx = 0; parent_idx < start_element.parents.size(); ++ parent_idx) {
|
||||
@ -741,7 +1170,7 @@ void organic_draw_branches(
|
||||
TreeVisitor::visit_recursive(move_bounds, start_element, trees.back());
|
||||
assert(!trees.back().branches.empty());
|
||||
//FIXME debugging
|
||||
#if 1
|
||||
#if 0
|
||||
if (start_element.state.lost) {
|
||||
}
|
||||
else if (start_element.state.verylost) {
|
||||
@ -758,7 +1187,7 @@ void organic_draw_branches(
|
||||
mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive;
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size(), 1),
|
||||
[&trees, &volumes, &config, &slicing_params, &move_bounds, &interface_placer, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
|
||||
[&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
|
||||
indexed_triangle_set partial_mesh;
|
||||
std::vector<float> slice_z;
|
||||
std::vector<Polygons> bottom_contacts;
|
||||
@ -816,7 +1245,7 @@ void organic_draw_branches(
|
||||
double support_area_min_radius = M_PI * sqr(double(config.branch_radius));
|
||||
double support_area_stop = std::max(0.2 * M_PI * sqr(double(bottom_radius)), 0.5 * support_area_min_radius);
|
||||
// Only propagate until the rest area is smaller than this threshold.
|
||||
double support_area_min = 0.1 * support_area_min_radius;
|
||||
//double support_area_min = 0.1 * support_area_min_radius;
|
||||
for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) {
|
||||
rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false));
|
||||
double rest_support_area = area(rest_support);
|
||||
@ -895,11 +1324,11 @@ void organic_draw_branches(
|
||||
Slice &dst = tree.slices[i - new_begin];
|
||||
if (++ dst.num_branches > 1) {
|
||||
append(dst.polygons, std::move(src));
|
||||
if (j < bottom_contacts.size())
|
||||
if (j < int(bottom_contacts.size()))
|
||||
append(dst.bottom_contacts, std::move(bottom_contacts[j]));
|
||||
} else {
|
||||
dst.polygons = std::move(std::move(src));
|
||||
if (j < bottom_contacts.size())
|
||||
if (j < int(bottom_contacts.size()))
|
||||
dst.bottom_contacts = std::move(bottom_contacts[j]);
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
|
||||
else {
|
||||
SupportGeneratorLayersPtr out(in1.size() + in2.size(), nullptr);
|
||||
std::merge(in1.begin(), in1.end(), in2.begin(), in2.end(), out.begin(), [](auto* l, auto* r) { return l->print_z < r->print_z; });
|
||||
return std::move(out);
|
||||
return out;
|
||||
}
|
||||
};
|
||||
interface_layers = merge_remove_empty(interface_layers, top_interface_layers);
|
||||
@ -664,7 +664,7 @@ static inline void tree_supports_generate_paths(
|
||||
// Draw the perimeters.
|
||||
Polylines polylines;
|
||||
polylines.reserve(expoly.holes.size() + 1);
|
||||
for (size_t idx_loop = 0; idx_loop < expoly.num_contours(); ++ idx_loop) {
|
||||
for (int idx_loop = 0; idx_loop < int(expoly.num_contours()); ++ idx_loop) {
|
||||
// Open the loop with a seam.
|
||||
const Polygon &loop = expoly.contour_or_hole(idx_loop);
|
||||
Polyline pl(loop.points);
|
||||
@ -680,11 +680,11 @@ static inline void tree_supports_generate_paths(
|
||||
ClipperLib_Z::Path *closest_contour = nullptr;
|
||||
Vec2d closest_point;
|
||||
int closest_point_idx = -1;
|
||||
double closest_point_t;
|
||||
double closest_point_t = 0.;
|
||||
double d2min = std::numeric_limits<double>::max();
|
||||
Vec2d seam_pt = pl.back().cast<double>();
|
||||
for (ClipperLib_Z::Path &path : anchor_candidates)
|
||||
for (int i = 0; i < path.size(); ++ i) {
|
||||
for (int i = 0; i < int(path.size()); ++ i) {
|
||||
int j = next_idx_modulo(i, path);
|
||||
if (path[i].z() == idx_loop || path[j].z() == idx_loop) {
|
||||
Vec2d pi(path[i].x(), path[i].y());
|
||||
|
@ -240,7 +240,7 @@ private:
|
||||
*/
|
||||
std::optional<std::reference_wrapper<const Polygons>> getArea(const TreeModelVolumes::RadiusLayerPair &key) const {
|
||||
std::lock_guard<std::mutex> guard(m_mutex);
|
||||
if (key.second >= m_data.size())
|
||||
if (key.second >= LayerIndex(m_data.size()))
|
||||
return std::optional<std::reference_wrapper<const Polygons>>{};
|
||||
const auto &layer = m_data[key.second];
|
||||
auto it = layer.find(key.first);
|
||||
@ -250,7 +250,7 @@ private:
|
||||
// Get a collision area at a given layer for a radius that is a lower or equial to the key radius.
|
||||
std::optional<std::pair<coord_t, std::reference_wrapper<const Polygons>>> get_lower_bound_area(const TreeModelVolumes::RadiusLayerPair &key) const {
|
||||
std::lock_guard<std::mutex> guard(m_mutex);
|
||||
if (key.second >= m_data.size())
|
||||
if (key.second >= LayerIndex(m_data.size()))
|
||||
return {};
|
||||
const auto &layer = m_data[key.second];
|
||||
if (layer.empty())
|
||||
|
@ -211,7 +211,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
//FIXME this is a fudge constant!
|
||||
auto enforcer_overhang_offset = scaled<double>(config.support_tree_tip_diameter.value);
|
||||
|
||||
size_t num_overhang_layers = support_auto ? num_object_layers : std::max(size_t(support_enforce_layers), enforcers_layers.size());
|
||||
size_t num_overhang_layers = support_auto ? num_object_layers : std::min(num_object_layers, std::max(size_t(support_enforce_layers), enforcers_layers.size()));
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(1, num_overhang_layers),
|
||||
[&print_object, &config, &print_config, &enforcers_layers, &blockers_layers,
|
||||
support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, &throw_on_cancel, &out]
|
||||
@ -387,6 +387,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
return result;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*!
|
||||
* \brief Converts lines in internal format into a Polygons object representing these lines.
|
||||
*
|
||||
@ -405,6 +406,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
validate_range(result);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Evaluates if a point has to be added now. Required for a split_lines call in generate_initial_areas().
|
||||
@ -788,7 +790,7 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
|
||||
else
|
||||
do_final_difference = true;
|
||||
}
|
||||
if (steps + (distance < last_step_offset_without_check || distance % step_size != 0) < min_amount_offset && min_amount_offset > 1) {
|
||||
if (steps + (distance < last_step_offset_without_check || (distance % step_size) != 0) < int(min_amount_offset) && min_amount_offset > 1) {
|
||||
// yes one can add a bool as the standard specifies that a result from compare operators has to be 0 or 1
|
||||
// reduce the stepsize to ensure it is offset the required amount of times
|
||||
step_size = distance / min_amount_offset;
|
||||
@ -1175,7 +1177,7 @@ void sample_overhang_area(
|
||||
}
|
||||
|
||||
assert(dtt_roof <= layer_idx);
|
||||
if (int(dtt_roof) >= layer_idx && large_horizontal_roof)
|
||||
if (dtt_roof >= layer_idx && large_horizontal_roof)
|
||||
// Reached buildplate when generating contact, interface and base interface layers.
|
||||
interface_placer.add_roof_build_plate(std::move(overhang_area), dtt_roof);
|
||||
else {
|
||||
@ -1282,7 +1284,7 @@ static void generate_initial_areas(
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, raw_overhangs.size()),
|
||||
[&volumes, &config, &raw_overhangs, &mesh_group_settings,
|
||||
min_xy_dist, force_tip_to_roof, roof_enabled, num_support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length, max_overhang_insert_lag,
|
||||
min_xy_dist, roof_enabled, num_support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length,
|
||||
&rich_interface_placer, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t raw_overhang_idx = range.begin(); raw_overhang_idx < range.end(); ++ raw_overhang_idx) {
|
||||
size_t layer_idx = raw_overhangs[raw_overhang_idx].first;
|
||||
@ -2611,13 +2613,13 @@ static void remove_deleted_elements(std::vector<SupportElements> &move_bounds)
|
||||
std::iota(map_current.begin(), map_current.end(), 0);
|
||||
}
|
||||
// Delete all "deleted" elements from the end of the layer vector.
|
||||
while (i < layer.size() && layer.back().state.deleted) {
|
||||
while (i < int32_t(layer.size()) && layer.back().state.deleted) {
|
||||
layer.pop_back();
|
||||
// Mark as deleted in the map.
|
||||
map_current[layer.size()] = -1;
|
||||
}
|
||||
assert(i == layer.size() || i + 1 < layer.size());
|
||||
if (i + 1 < layer.size()) {
|
||||
if (i + 1 < int32_t(layer.size())) {
|
||||
element = std::move(layer.back());
|
||||
layer.pop_back();
|
||||
// Mark the current element as deleted.
|
||||
@ -2667,7 +2669,7 @@ static void create_nodes_from_area(
|
||||
|
||||
for (LayerIndex layer_idx = 1; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
|
||||
auto &layer = move_bounds[layer_idx];
|
||||
auto *layer_above = layer_idx + 1 < move_bounds.size() ? &move_bounds[layer_idx + 1] : nullptr;
|
||||
auto *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
|
||||
if (layer_above)
|
||||
for (SupportElement &elem : *layer_above)
|
||||
elem.state.marked = false;
|
||||
@ -2809,7 +2811,7 @@ static void generate_branch_areas(
|
||||
const Point movement = draw_area.child_element->state.result_on_layer - draw_area.element->state.result_on_layer;
|
||||
movement_directions.emplace_back(movement, radius);
|
||||
}
|
||||
const SupportElements *layer_above = layer_idx + 1 < move_bounds.size() ? &move_bounds[layer_idx + 1] : nullptr;
|
||||
const SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
|
||||
for (int32_t parent_idx : draw_area.element->parents) {
|
||||
const SupportElement &parent = (*layer_above)[parent_idx];
|
||||
const Point movement = parent.state.result_on_layer - draw_area.element->state.result_on_layer;
|
||||
@ -3255,7 +3257,7 @@ static void draw_areas(
|
||||
std::vector<std::pair<SupportElement*, SupportElement*>> map_downwards_old;
|
||||
std::vector<std::pair<SupportElement*, SupportElement*>> map_downwards_new;
|
||||
for (LayerIndex layer_idx = 0; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
|
||||
SupportElements *layer_above = layer_idx + 1 < move_bounds.size() ? &move_bounds[layer_idx + 1] : nullptr;
|
||||
SupportElements *layer_above = layer_idx + 1 < LayerIndex(move_bounds.size()) ? &move_bounds[layer_idx + 1] : nullptr;
|
||||
map_downwards_new.clear();
|
||||
linear_data_layers.emplace_back(linear_data.size());
|
||||
std::sort(map_downwards_old.begin(), map_downwards_old.end(), [](auto &l, auto &r) { return l.first < r.first; });
|
||||
@ -3472,6 +3474,17 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
|
||||
if (support_params.has_base_interfaces() || has_raft)
|
||||
base_interface_layers.assign(num_support_layers, nullptr);
|
||||
|
||||
auto remove_undefined_layers = [&bottom_contacts, &top_contacts, &interface_layers, &base_interface_layers, &intermediate_layers]() {
|
||||
auto doit = [](SupportGeneratorLayersPtr& layers) {
|
||||
layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end());
|
||||
};
|
||||
doit(bottom_contacts);
|
||||
doit(top_contacts);
|
||||
doit(interface_layers);
|
||||
doit(base_interface_layers);
|
||||
doit(intermediate_layers);
|
||||
};
|
||||
|
||||
InterfacePlacer interface_placer{
|
||||
print_object.slicing_parameters(), support_params, config,
|
||||
// Outputs
|
||||
@ -3523,14 +3536,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
|
||||
throw_on_cancel);
|
||||
}
|
||||
|
||||
auto remove_undefined_layers = [](SupportGeneratorLayersPtr& layers) {
|
||||
layers.erase(std::remove_if(layers.begin(), layers.end(), [](const SupportGeneratorLayer* ptr) { return ptr == nullptr; }), layers.end());
|
||||
};
|
||||
remove_undefined_layers(bottom_contacts);
|
||||
remove_undefined_layers(top_contacts);
|
||||
remove_undefined_layers(interface_layers);
|
||||
remove_undefined_layers(base_interface_layers);
|
||||
remove_undefined_layers(intermediate_layers);
|
||||
remove_undefined_layers();
|
||||
|
||||
std::tie(interface_layers, base_interface_layers) = generate_interface_layers(print_object.config(), support_params,
|
||||
bottom_contacts, top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
|
||||
@ -3553,7 +3559,9 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
|
||||
// BOOST_LOG_TRIVIAL(error) << "Why ask questions when you already know the answer twice.\n (This is not a real bug, please dont report it.)";
|
||||
|
||||
move_bounds.clear();
|
||||
} else if (generate_raft_contact(print_object, config, interface_placer) < 0)
|
||||
} else if (generate_raft_contact(print_object, config, interface_placer) >= 0) {
|
||||
remove_undefined_layers();
|
||||
} else
|
||||
// No raft.
|
||||
continue;
|
||||
|
||||
|
@ -78,14 +78,12 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr
|
||||
}
|
||||
|
||||
TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params)
|
||||
: angle(mesh_group_settings.support_tree_angle),
|
||||
angle_slow(mesh_group_settings.support_tree_angle_slow),
|
||||
support_line_width(mesh_group_settings.support_line_width),
|
||||
: support_line_width(mesh_group_settings.support_line_width),
|
||||
layer_height(mesh_group_settings.layer_height),
|
||||
branch_radius(mesh_group_settings.support_tree_branch_diameter / 2),
|
||||
min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance
|
||||
maximum_move_distance((angle < M_PI / 2.) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits<coord_t>::max()),
|
||||
maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
|
||||
maximum_move_distance((mesh_group_settings.support_tree_angle < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle) * layer_height) : std::numeric_limits<coord_t>::max()),
|
||||
maximum_move_distance_slow((mesh_group_settings.support_tree_angle_slow < M_PI / 2.) ? (coord_t)(tan(mesh_group_settings.support_tree_angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
|
||||
support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0),
|
||||
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large
|
||||
branch_radius_increase_per_layer(tan(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height),
|
||||
@ -155,7 +153,7 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings &mes
|
||||
// Layers between the raft contacts and bottom of the object.
|
||||
auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height));
|
||||
double step = dist_to_go / nsteps;
|
||||
for (size_t i = 0; i < nsteps; ++ i) {
|
||||
for (int i = 0; i < nsteps; ++ i) {
|
||||
z += step;
|
||||
this->raft_layers.emplace_back(z);
|
||||
}
|
||||
|
@ -443,8 +443,6 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
double angle;
|
||||
double angle_slow;
|
||||
// std::vector<coord_t> known_z;
|
||||
};
|
||||
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/predef/other/endian.h>
|
||||
|
||||
#include <tbb/concurrent_vector.h>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Dense>
|
||||
|
||||
@ -871,11 +873,38 @@ void its_collect_mesh_projection_points_above(const indexed_triangle_set &its, c
|
||||
}
|
||||
|
||||
template<typename TransformVertex>
|
||||
Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const TransformVertex &transform_fn, const float z)
|
||||
Polygon its_convex_hull_2d_above(const indexed_triangle_set& its, const TransformVertex& transform_fn, const float z)
|
||||
{
|
||||
Points all_pts;
|
||||
its_collect_mesh_projection_points_above(its, transform_fn, z, all_pts);
|
||||
return Geometry::convex_hull(std::move(all_pts));
|
||||
auto collect_mesh_projection_points_above = [&](const tbb::blocked_range<size_t>& range) {
|
||||
Points pts;
|
||||
pts.reserve(range.size() * 4); // there can be up to 4 vertices per triangle
|
||||
for (size_t i = range.begin(); i < range.end(); ++i) {
|
||||
const stl_triangle_vertex_indices& tri = its.indices[i];
|
||||
const Vec3f tri_pts[3] = { transform_fn(its.vertices[tri(0)]), transform_fn(its.vertices[tri(1)]), transform_fn(its.vertices[tri(2)]) };
|
||||
int iprev = 2;
|
||||
for (int iedge = 0; iedge < 3; ++iedge) {
|
||||
const Vec3f& p1 = tri_pts[iprev];
|
||||
const Vec3f& p2 = tri_pts[iedge];
|
||||
if ((p1.z() < z && p2.z() > z) || (p2.z() < z && p1.z() > z)) {
|
||||
// Edge crosses the z plane. Calculate intersection point with the plane.
|
||||
const float t = (z - p1.z()) / (p2.z() - p1.z());
|
||||
pts.emplace_back(scaled<coord_t>(p1.x() + (p2.x() - p1.x()) * t), scaled<coord_t>(p1.y() + (p2.y() - p1.y()) * t));
|
||||
}
|
||||
if (p2.z() >= z)
|
||||
pts.emplace_back(scaled<coord_t>(p2.x()), scaled<coord_t>(p2.y()));
|
||||
iprev = iedge;
|
||||
}
|
||||
}
|
||||
return Geometry::convex_hull(std::move(pts));
|
||||
};
|
||||
|
||||
tbb::concurrent_vector<Polygon> chs;
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, its.indices.size()), [&](const tbb::blocked_range<size_t>& range) {
|
||||
chs.push_back(collect_mesh_projection_points_above(range));
|
||||
});
|
||||
|
||||
const Polygons polygons(chs.begin(), chs.end());
|
||||
return Geometry::convex_hull(polygons);
|
||||
}
|
||||
|
||||
Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Matrix3f &m, const float z)
|
||||
|
@ -833,64 +833,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
||||
}
|
||||
}
|
||||
|
||||
bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, ModelInstanceEPrintVolumeState *out_state) const
|
||||
{
|
||||
const Model& model = GUI::wxGetApp().plater()->model();
|
||||
auto volume_below = [](GLVolume& volume) -> bool
|
||||
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); };
|
||||
// Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used.
|
||||
auto volume_sinking = [](GLVolume& volume) -> bool
|
||||
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_sinking(); };
|
||||
// Cached bounding box of a volume above the print bed.
|
||||
auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3
|
||||
{ return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); };
|
||||
// Cached 3D convex hull of a volume above the print bed.
|
||||
auto volume_convex_mesh = [volume_sinking, &model](GLVolume& volume) -> const TriangleMesh&
|
||||
{ return volume_sinking(volume) ? model.objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); };
|
||||
|
||||
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
|
||||
bool contained_min_one = false;
|
||||
|
||||
for (GLVolume* volume : this->volumes)
|
||||
if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
|
||||
BuildVolume::ObjectState state;
|
||||
if (volume_below(*volume))
|
||||
state = BuildVolume::ObjectState::Below;
|
||||
else {
|
||||
switch (build_volume.type()) {
|
||||
case BuildVolume::Type::Rectangle:
|
||||
//FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects.
|
||||
state = build_volume.volume_state_bbox(volume_bbox(*volume));
|
||||
break;
|
||||
case BuildVolume::Type::Circle:
|
||||
case BuildVolume::Type::Convex:
|
||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||
case BuildVolume::Type::Custom:
|
||||
state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), volume_sinking(*volume));
|
||||
break;
|
||||
default:
|
||||
// Ignore, don't produce any collision.
|
||||
state = BuildVolume::ObjectState::Inside;
|
||||
break;
|
||||
}
|
||||
assert(state != BuildVolume::ObjectState::Below);
|
||||
}
|
||||
volume->is_outside = state != BuildVolume::ObjectState::Inside;
|
||||
if (volume->printable) {
|
||||
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
|
||||
overall_state = ModelInstancePVS_Fully_Outside;
|
||||
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding)
|
||||
overall_state = ModelInstancePVS_Partly_Outside;
|
||||
contained_min_one |= !volume->is_outside;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_state != nullptr)
|
||||
*out_state = overall_state;
|
||||
|
||||
return contained_min_one;
|
||||
}
|
||||
|
||||
void GLVolumeCollection::reset_outside_state()
|
||||
{
|
||||
for (GLVolume* volume : this->volumes) {
|
||||
|
@ -450,9 +450,6 @@ public:
|
||||
void set_show_sinking_contours(bool show) { m_show_sinking_contours = show; }
|
||||
void set_show_non_manifold_edges(bool show) { m_show_non_manifold_edges = show; }
|
||||
|
||||
// returns true if all the volumes are completely contained in the print volume
|
||||
// returns the containment state in the given out_state, if non-null
|
||||
bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
|
||||
void reset_outside_state();
|
||||
|
||||
void update_colors_by_extruder(const DynamicPrintConfig* config);
|
||||
|
@ -3575,9 +3575,6 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
int old_view_type = static_cast<int>(get_view_type());
|
||||
int view_type = old_view_type;
|
||||
|
||||
if (!m_legend_resizer.dirty)
|
||||
ImGui::SetNextItemWidth(-1.0f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, { 0.1f, 0.1f, 0.1f, 0.8f });
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, { 0.2f, 0.2f, 0.2f, 0.8f });
|
||||
imgui.combo(std::string(), { _u8L("Feature type"),
|
||||
@ -3590,7 +3587,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
_u8L("Layer time (linear)"),
|
||||
_u8L("Layer time (logarithmic)"),
|
||||
_u8L("Tool"),
|
||||
_u8L("Color Print") }, view_type, ImGuiComboFlags_HeightLargest);
|
||||
_u8L("Color Print") }, view_type, ImGuiComboFlags_HeightLargest, 0.0f, -1.0f);
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
if (old_view_type != view_type) {
|
||||
@ -3961,7 +3958,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
||||
const auto custom_it = std::find(m_roles.begin(), m_roles.end(), GCodeExtrusionRole::Custom);
|
||||
if (custom_it != m_roles.end()) {
|
||||
const bool custom_visible = is_visible(GCodeExtrusionRole::Custom);
|
||||
const wxString btn_text = custom_visible ? _u8L("Hide Custom GCode") : _u8L("Show Custom GCode");
|
||||
const wxString btn_text = custom_visible ? _u8L("Hide Custom G-code") : _u8L("Show Custom G-code");
|
||||
ImGui::Separator();
|
||||
if (imgui.button(btn_text, ImVec2(-1.0f, 0.0f), true)) {
|
||||
m_extrusions.role_visibility_flags = custom_visible ? m_extrusions.role_visibility_flags & ~(1 << int(GCodeExtrusionRole::Custom)) :
|
||||
|
@ -132,6 +132,9 @@ void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config)
|
||||
delete m_slicing_parameters;
|
||||
m_slicing_parameters = nullptr;
|
||||
m_layers_texture.valid = false;
|
||||
|
||||
m_layer_height_profile.clear();
|
||||
m_layer_height_profile_modified = false;
|
||||
}
|
||||
|
||||
void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id)
|
||||
@ -141,6 +144,7 @@ void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id)
|
||||
// Changing maximum height of an object will invalidate the layer heigth editing profile.
|
||||
// m_model_object->bounding_box() is cached, therefore it is cheap even if this method is called frequently.
|
||||
const float new_max_z = (model_object_new == nullptr) ? 0.0f : static_cast<float>(model_object_new->max_z());
|
||||
|
||||
if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z ||
|
||||
(model_object_new != nullptr && m_model_object->id() != model_object_new->id())) {
|
||||
m_layer_height_profile.clear();
|
||||
@ -543,10 +547,10 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
|
||||
|
||||
void GLCanvas3D::LayersEditing::adjust_layer_height_profile()
|
||||
{
|
||||
this->update_slicing_parameters();
|
||||
PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile);
|
||||
Slic3r::adjust_layer_height_profile(*m_slicing_parameters, m_layer_height_profile, this->last_z, this->strength, this->band_width, this->last_action);
|
||||
m_layer_height_profile_modified = true;
|
||||
this->update_slicing_parameters();
|
||||
PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile);
|
||||
Slic3r::adjust_layer_height_profile(*m_slicing_parameters, m_layer_height_profile, this->last_z, this->strength, this->band_width, this->last_action);
|
||||
m_layer_height_profile_modified = true;
|
||||
m_layers_texture.valid = false;
|
||||
}
|
||||
|
||||
@ -581,14 +585,15 @@ void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas,
|
||||
|
||||
void GLCanvas3D::LayersEditing::generate_layer_height_texture()
|
||||
{
|
||||
this->update_slicing_parameters();
|
||||
// Always try to update the layer height profile.
|
||||
this->update_slicing_parameters();
|
||||
// Always try to update the layer height profile.
|
||||
bool update = ! m_layers_texture.valid;
|
||||
if (PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile)) {
|
||||
// Initialized to the default value.
|
||||
m_layer_height_profile_modified = false;
|
||||
update = true;
|
||||
}
|
||||
|
||||
// Update if the layer height profile was changed, or when the texture is not valid.
|
||||
if (! update && ! m_layers_texture.data.empty() && m_layers_texture.cells > 0)
|
||||
// Texture is valid, don't update.
|
||||
@ -615,8 +620,8 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas)
|
||||
if (m_layer_height_profile_modified) {
|
||||
wxGetApp().plater()->take_snapshot(_L("Variable layer height - Manual edit"));
|
||||
const_cast<ModelObject*>(m_model_object)->layer_height_profile.set(m_layer_height_profile);
|
||||
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||
wxGetApp().obj_list()->update_info_items(last_object_id);
|
||||
wxGetApp().plater()->schedule_background_process();
|
||||
}
|
||||
}
|
||||
m_layer_height_profile_modified = false;
|
||||
@ -880,20 +885,22 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas
|
||||
ImGui::PopStyleVar(2);
|
||||
}
|
||||
|
||||
void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons)
|
||||
void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& contours, bool generate_fill)
|
||||
{
|
||||
m_perimeter.reset();
|
||||
m_contours.clear();
|
||||
m_instances.clear();
|
||||
m_fill.reset();
|
||||
if (polygons.empty())
|
||||
|
||||
if (contours.empty())
|
||||
return;
|
||||
|
||||
if (m_render_fill) {
|
||||
if (generate_fill) {
|
||||
GLModel::Geometry fill_data;
|
||||
fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
|
||||
fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f };
|
||||
fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f };
|
||||
|
||||
// vertices + indices
|
||||
const ExPolygons polygons_union = union_ex(polygons);
|
||||
const ExPolygons polygons_union = union_ex(contours.contours);
|
||||
unsigned int vertices_counter = 0;
|
||||
for (const ExPolygon& poly : polygons_union) {
|
||||
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(poly);
|
||||
@ -906,17 +913,48 @@ void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons
|
||||
fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
|
||||
}
|
||||
}
|
||||
|
||||
m_fill.init_from(std::move(fill_data));
|
||||
}
|
||||
|
||||
m_perimeter.init_from(polygons, 0.025f); // add a small positive z to avoid z-fighting
|
||||
for (size_t i = 0; i < contours.contours.size(); ++i) {
|
||||
GLModel& model = m_contours.emplace_back(GLModel());
|
||||
model.init_from(contours.contours[i], 0.025f); // add a small positive z to avoid z-fighting
|
||||
}
|
||||
|
||||
if (contours.trafos.has_value()) {
|
||||
// create the requested instances
|
||||
for (const auto& instance : *contours.trafos) {
|
||||
m_instances.emplace_back(instance.first, instance.second);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// no instances have been specified
|
||||
// create one instance for every polygon
|
||||
for (size_t i = 0; i < contours.contours.size(); ++i) {
|
||||
m_instances.emplace_back(i, Transform3f::Identity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLCanvas3D::SequentialPrintClearance::update_instances_trafos(const std::vector<Transform3d>& trafos)
|
||||
{
|
||||
if (trafos.size() == m_instances.size()) {
|
||||
for (size_t i = 0; i < trafos.size(); ++i) {
|
||||
m_instances[i].second = trafos[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void GLCanvas3D::SequentialPrintClearance::render()
|
||||
{
|
||||
const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f };
|
||||
const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f };
|
||||
const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f };
|
||||
const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f };
|
||||
const ColorRGBA NO_FILL_EVALUATING_COLOR = { 1.0f, 1.0f, 0.0f, 1.0f };
|
||||
|
||||
if (m_contours.empty() || m_instances.empty())
|
||||
return;
|
||||
|
||||
GLShaderProgram* shader = wxGetApp().get_shader("flat");
|
||||
if (shader == nullptr)
|
||||
@ -933,9 +971,34 @@ void GLCanvas3D::SequentialPrintClearance::render()
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
|
||||
m_perimeter.set_color(m_render_fill ? FILL_COLOR : NO_FILL_COLOR);
|
||||
m_perimeter.render();
|
||||
m_fill.render();
|
||||
if (!m_evaluating)
|
||||
m_fill.render();
|
||||
|
||||
#if ENABLE_GL_CORE_PROFILE
|
||||
if (OpenGLManager::get_gl_info().is_core_profile()) {
|
||||
shader->stop_using();
|
||||
|
||||
shader = wxGetApp().get_shader("dashed_thick_lines");
|
||||
if (shader == nullptr)
|
||||
return;
|
||||
|
||||
shader->start_using();
|
||||
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
shader->set_uniform("viewport_size", Vec2d(double(viewport[2]), double(viewport[3])));
|
||||
shader->set_uniform("width", 1.0f);
|
||||
shader->set_uniform("gap_size", 0.0f);
|
||||
}
|
||||
else
|
||||
#endif // ENABLE_GL_CORE_PROFILE
|
||||
glsafe(::glLineWidth(2.0f));
|
||||
|
||||
for (const auto& [id, trafo] : m_instances) {
|
||||
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo);
|
||||
assert(id < m_contours.size());
|
||||
m_contours[id].set_color((!m_evaluating && m_fill.is_initialized()) ? FILL_COLOR : m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR);
|
||||
m_contours[id].render();
|
||||
}
|
||||
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
glsafe(::glEnable(GL_CULL_FACE));
|
||||
@ -1240,7 +1303,7 @@ void GLCanvas3D::SLAView::render_switch_button()
|
||||
imgui.begin(std::string("SLAViewSwitch"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
|
||||
const float icon_size = 1.5 * ImGui::GetTextLineHeight();
|
||||
if (imgui.draw_radio_button(_u8L("SLA view"), 1.5f * icon_size, true,
|
||||
[this, &imgui, sel_instance](ImGuiWindow& window, const ImVec2& pos, float size) {
|
||||
[&imgui, sel_instance](ImGuiWindow& window, const ImVec2& pos, float size) {
|
||||
const wchar_t icon_id = (sel_instance->second == ESLAViewType::Original) ? ImGui::SlaViewProcessed : ImGui::SlaViewOriginal;
|
||||
imgui.draw_icon(window, pos, size, icon_id);
|
||||
})) {
|
||||
@ -1450,14 +1513,97 @@ void GLCanvas3D::reset_volumes()
|
||||
_set_warning_notification(EWarning::ObjectOutside, false);
|
||||
}
|
||||
|
||||
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const
|
||||
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state(bool selection_only) const
|
||||
{
|
||||
ModelInstanceEPrintVolumeState state = ModelInstanceEPrintVolumeState::ModelInstancePVS_Inside;
|
||||
if (m_initialized)
|
||||
m_volumes.check_outside_state(m_bed.build_volume(), &state);
|
||||
if (m_initialized && !m_volumes.empty())
|
||||
check_volumes_outside_state(m_bed.build_volume(), &state, selection_only);
|
||||
return state;
|
||||
}
|
||||
|
||||
bool GLCanvas3D::check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state, bool selection_only) const
|
||||
{
|
||||
auto volume_below = [](GLVolume& volume) -> bool
|
||||
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); };
|
||||
// Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used.
|
||||
auto volume_sinking = [](GLVolume& volume) -> bool
|
||||
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_sinking(); };
|
||||
// Cached bounding box of a volume above the print bed.
|
||||
auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3
|
||||
{ return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); };
|
||||
// Cached 3D convex hull of a volume above the print bed.
|
||||
auto volume_convex_mesh = [this, volume_sinking](GLVolume& volume) -> const TriangleMesh&
|
||||
{ return volume_sinking(volume) ? m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); };
|
||||
|
||||
auto volumes_to_process_idxs = [this, selection_only]() {
|
||||
std::vector<unsigned int> ret;
|
||||
if (!selection_only || m_selection.is_empty()) {
|
||||
ret = std::vector<unsigned int>(m_volumes.volumes.size());
|
||||
std::iota(ret.begin(), ret.end(), 0);
|
||||
}
|
||||
else {
|
||||
const GUI::Selection::IndicesList& selected_volume_idxs = m_selection.get_volume_idxs();
|
||||
ret.assign(selected_volume_idxs.begin(), selected_volume_idxs.end());
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
|
||||
bool contained_min_one = false;
|
||||
|
||||
const std::vector<unsigned int> volumes_idxs = volumes_to_process_idxs();
|
||||
|
||||
for (unsigned int vol_idx : volumes_idxs) {
|
||||
GLVolume* volume = m_volumes.volumes[vol_idx];
|
||||
if (!volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (!volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
|
||||
BuildVolume::ObjectState state;
|
||||
if (volume_below(*volume))
|
||||
state = BuildVolume::ObjectState::Below;
|
||||
else {
|
||||
switch (build_volume.type()) {
|
||||
case BuildVolume::Type::Rectangle:
|
||||
//FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects.
|
||||
state = build_volume.volume_state_bbox(volume_bbox(*volume));
|
||||
break;
|
||||
case BuildVolume::Type::Circle:
|
||||
case BuildVolume::Type::Convex:
|
||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||
case BuildVolume::Type::Custom:
|
||||
state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), volume_sinking(*volume));
|
||||
break;
|
||||
default:
|
||||
// Ignore, don't produce any collision.
|
||||
state = BuildVolume::ObjectState::Inside;
|
||||
break;
|
||||
}
|
||||
assert(state != BuildVolume::ObjectState::Below);
|
||||
}
|
||||
volume->is_outside = state != BuildVolume::ObjectState::Inside;
|
||||
if (volume->printable) {
|
||||
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
|
||||
overall_state = ModelInstancePVS_Fully_Outside;
|
||||
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding)
|
||||
overall_state = ModelInstancePVS_Partly_Outside;
|
||||
contained_min_one |= !volume->is_outside;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int vol_idx = 0; vol_idx < m_volumes.volumes.size(); ++vol_idx) {
|
||||
if (std::find(volumes_idxs.begin(), volumes_idxs.end(), vol_idx) == volumes_idxs.end()) {
|
||||
if (!m_volumes.volumes[vol_idx]->is_outside) {
|
||||
contained_min_one = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (out_state != nullptr)
|
||||
*out_state = overall_state;
|
||||
|
||||
return contained_min_one;
|
||||
}
|
||||
|
||||
void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx)
|
||||
{
|
||||
if (current_printer_technology() != ptSLA)
|
||||
@ -2464,7 +2610,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
// checks for geometry outside the print volume to render it accordingly
|
||||
if (!m_volumes.empty()) {
|
||||
ModelInstanceEPrintVolumeState state;
|
||||
const bool contained_min_one = m_volumes.check_outside_state(m_bed.build_volume(), &state);
|
||||
const bool contained_min_one = check_volumes_outside_state(m_bed.build_volume(), &state, !force_full_scene_refresh);
|
||||
const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside);
|
||||
const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside);
|
||||
|
||||
@ -3430,17 +3576,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
// Not only detection of some modifiers !!!
|
||||
if (evt.Dragging()) {
|
||||
GLGizmosManager::EType c = m_gizmos.get_current_type();
|
||||
if (current_printer_technology() == ptFFF &&
|
||||
fff_print()->config().complete_objects){
|
||||
if (c == GLGizmosManager::EType::Move ||
|
||||
c == GLGizmosManager::EType::Scale ||
|
||||
c == GLGizmosManager::EType::Rotate )
|
||||
update_sequential_clearance();
|
||||
} else {
|
||||
if (c == GLGizmosManager::EType::Move ||
|
||||
c == GLGizmosManager::EType::Scale ||
|
||||
c == GLGizmosManager::EType::Rotate)
|
||||
show_sinking_contours();
|
||||
if (c == GLGizmosManager::EType::Move ||
|
||||
c == GLGizmosManager::EType::Scale ||
|
||||
c == GLGizmosManager::EType::Rotate) {
|
||||
show_sinking_contours();
|
||||
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects)
|
||||
update_sequential_clearance(true);
|
||||
}
|
||||
}
|
||||
else if (evt.LeftUp() &&
|
||||
@ -3643,7 +3784,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
trafo_type.set_relative();
|
||||
m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type);
|
||||
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects)
|
||||
update_sequential_clearance();
|
||||
update_sequential_clearance(false);
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
m_dirty = true;
|
||||
}
|
||||
@ -3949,7 +4090,10 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
||||
if (wipe_tower_origin != Vec3d::Zero())
|
||||
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin)));
|
||||
|
||||
reset_sequential_print_clearance();
|
||||
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
|
||||
update_sequential_clearance(true);
|
||||
m_sequential_print_clearance.m_evaluating = true;
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
@ -4034,6 +4178,11 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
||||
if (!done.empty())
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
|
||||
|
||||
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
|
||||
update_sequential_clearance(true);
|
||||
m_sequential_print_clearance.m_evaluating = true;
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
@ -4106,6 +4255,11 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
|
||||
if (!done.empty())
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
|
||||
|
||||
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
|
||||
update_sequential_clearance(true);
|
||||
m_sequential_print_clearance.m_evaluating = true;
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
@ -4373,16 +4527,33 @@ void GLCanvas3D::mouse_up_cleanup()
|
||||
m_canvas->ReleaseMouse();
|
||||
}
|
||||
|
||||
void GLCanvas3D::update_sequential_clearance()
|
||||
void GLCanvas3D::update_sequential_clearance(bool force_contours_generation)
|
||||
{
|
||||
if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects)
|
||||
return;
|
||||
|
||||
if (m_layers_editing.is_enabled() || m_gizmos.is_dragging())
|
||||
if (m_layers_editing.is_enabled())
|
||||
return;
|
||||
|
||||
auto instance_transform_from_volumes = [this](int object_idx, int instance_idx) {
|
||||
for (const GLVolume* v : m_volumes.volumes) {
|
||||
if (v->object_idx() == object_idx && v->instance_idx() == instance_idx)
|
||||
return v->get_instance_transformation();
|
||||
}
|
||||
assert(false);
|
||||
return Geometry::Transformation();
|
||||
};
|
||||
|
||||
auto is_object_outside_printbed = [this](int object_idx) {
|
||||
for (const GLVolume* v : m_volumes.volumes) {
|
||||
if (v->object_idx() == object_idx && v->is_outside)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// collects instance transformations from volumes
|
||||
// first define temporary cache
|
||||
// first: define temporary cache
|
||||
unsigned int instances_count = 0;
|
||||
std::vector<std::vector<std::optional<Geometry::Transformation>>> instance_transforms;
|
||||
for (size_t obj = 0; obj < m_model->objects.size(); ++obj) {
|
||||
@ -4397,67 +4568,94 @@ void GLCanvas3D::update_sequential_clearance()
|
||||
if (instances_count == 1)
|
||||
return;
|
||||
|
||||
// second fill temporary cache with data from volumes
|
||||
// second: fill temporary cache with data from volumes
|
||||
for (const GLVolume* v : m_volumes.volumes) {
|
||||
if (v->is_modifier || v->is_wipe_tower)
|
||||
if (v->is_wipe_tower)
|
||||
continue;
|
||||
|
||||
auto& transform = instance_transforms[v->object_idx()][v->instance_idx()];
|
||||
const int object_idx = v->object_idx();
|
||||
const int instance_idx = v->instance_idx();
|
||||
auto& transform = instance_transforms[object_idx][instance_idx];
|
||||
if (!transform.has_value())
|
||||
transform = v->get_instance_transformation();
|
||||
transform = instance_transform_from_volumes(object_idx, instance_idx);
|
||||
}
|
||||
|
||||
// helper function to calculate the transformation to be applied to the sequential print clearance contours
|
||||
auto instance_trafo = [](const Transform3d& hull_trafo, const Geometry::Transformation& inst_trafo) {
|
||||
Vec3d offset = inst_trafo.get_offset() - hull_trafo.translation();
|
||||
offset.z() = 0.0;
|
||||
return Geometry::translation_transform(offset) *
|
||||
Geometry::rotation_transform(Geometry::rotation_diff_z(hull_trafo, inst_trafo.get_matrix()) * Vec3d::UnitZ());
|
||||
};
|
||||
|
||||
// calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid())
|
||||
// this is done only the first time this method is called while moving the mouse,
|
||||
// the results are then cached for following displacements
|
||||
if (m_sequential_print_clearance_first_displacement) {
|
||||
m_sequential_print_clearance.m_hull_2d_cache.clear();
|
||||
if (force_contours_generation || m_sequential_print_clearance_first_displacement) {
|
||||
m_sequential_print_clearance.m_evaluating = false;
|
||||
m_sequential_print_clearance.m_hulls_2d_cache.clear();
|
||||
const float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
|
||||
const double mitter_limit = scale_(0.1);
|
||||
m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size());
|
||||
m_sequential_print_clearance.m_hulls_2d_cache.reserve(m_model->objects.size());
|
||||
for (size_t i = 0; i < m_model->objects.size(); ++i) {
|
||||
ModelObject* model_object = m_model->objects[i];
|
||||
ModelInstance* model_instance0 = model_object->instances.front();
|
||||
Geometry::Transformation trafo = model_instance0->get_transformation();
|
||||
trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() });
|
||||
const Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()),
|
||||
Geometry::Transformation trafo = instance_transform_from_volumes((int)i, 0);
|
||||
trafo.set_offset({ 0.0, 0.0, trafo.get_offset().z() });
|
||||
Pointf3s& new_hull_2d = m_sequential_print_clearance.m_hulls_2d_cache.emplace_back(std::make_pair(Pointf3s(), trafo.get_matrix())).first;
|
||||
if (is_object_outside_printbed((int)i))
|
||||
continue;
|
||||
|
||||
Polygon hull_2d = model_object->convex_hull_2d(trafo.get_matrix());
|
||||
if (!hull_2d.empty()) {
|
||||
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||
shrink_factor,
|
||||
jtRound, mitter_limit).front();
|
||||
const Polygons offset_res = offset(hull_2d, shrink_factor, jtRound, mitter_limit);
|
||||
if (!offset_res.empty())
|
||||
hull_2d = offset_res.front();
|
||||
}
|
||||
|
||||
Pointf3s& cache_hull_2d = m_sequential_print_clearance.m_hull_2d_cache.emplace_back(Pointf3s());
|
||||
cache_hull_2d.reserve(hull_2d.points.size());
|
||||
const Transform3d inv_trafo = trafo.get_matrix().inverse();
|
||||
new_hull_2d.reserve(hull_2d.points.size());
|
||||
for (const Point& p : hull_2d.points) {
|
||||
cache_hull_2d.emplace_back(inv_trafo * Vec3d(unscale<double>(p.x()), unscale<double>(p.y()), 0.0));
|
||||
new_hull_2d.emplace_back(Vec3d(unscale<double>(p.x()), unscale<double>(p.y()), 0.0));
|
||||
}
|
||||
}
|
||||
|
||||
ContoursList contours;
|
||||
contours.contours.reserve(instance_transforms.size());
|
||||
contours.trafos = std::vector<std::pair<size_t, Transform3d>>();
|
||||
(*contours.trafos).reserve(instances_count);
|
||||
for (size_t i = 0; i < instance_transforms.size(); ++i) {
|
||||
const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i];
|
||||
Points hull_pts;
|
||||
hull_pts.reserve(hull.size());
|
||||
for (size_t j = 0; j < hull.size(); ++j) {
|
||||
hull_pts.emplace_back(scaled<double>(hull[j].x()), scaled<double>(hull[j].y()));
|
||||
}
|
||||
contours.contours.emplace_back(Geometry::convex_hull(std::move(hull_pts)));
|
||||
|
||||
const auto& instances = instance_transforms[i];
|
||||
for (const auto& instance : instances) {
|
||||
(*contours.trafos).emplace_back(i, instance_trafo(hull_trafo, *instance));
|
||||
}
|
||||
}
|
||||
|
||||
set_sequential_print_clearance_contours(contours, false);
|
||||
m_sequential_print_clearance_first_displacement = false;
|
||||
}
|
||||
|
||||
// calculates instances 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid())
|
||||
Polygons polygons;
|
||||
polygons.reserve(instances_count);
|
||||
for (size_t i = 0; i < instance_transforms.size(); ++i) {
|
||||
const auto& instances = instance_transforms[i];
|
||||
for (const auto& instance : instances) {
|
||||
const Transform3d& trafo = instance->get_matrix();
|
||||
const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i];
|
||||
Points inst_pts;
|
||||
inst_pts.reserve(hull_2d.size());
|
||||
for (size_t j = 0; j < hull_2d.size(); ++j) {
|
||||
const Vec3d p = trafo * hull_2d[j];
|
||||
inst_pts.emplace_back(scaled<double>(p.x()), scaled<double>(p.y()));
|
||||
else {
|
||||
if (!m_sequential_print_clearance.empty()) {
|
||||
std::vector<Transform3d> trafos;
|
||||
trafos.reserve(instances_count);
|
||||
for (size_t i = 0; i < instance_transforms.size(); ++i) {
|
||||
const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i];
|
||||
const auto& instances = instance_transforms[i];
|
||||
for (const auto& instance : instances) {
|
||||
trafos.emplace_back(instance_trafo(hull_trafo, *instance));
|
||||
}
|
||||
}
|
||||
polygons.emplace_back(Geometry::convex_hull(std::move(inst_pts)));
|
||||
m_sequential_print_clearance.update_instances_trafos(trafos);
|
||||
}
|
||||
}
|
||||
|
||||
// sends instances 2d hulls to be rendered
|
||||
set_sequential_print_clearance_visible(true);
|
||||
set_sequential_print_clearance_render_fill(false);
|
||||
set_sequential_print_clearance_polygons(polygons);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::is_object_sinking(int object_idx) const
|
||||
@ -5937,7 +6135,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
|
||||
}
|
||||
}
|
||||
if (m_requires_check_outside_state) {
|
||||
m_volumes.check_outside_state(build_volume, nullptr);
|
||||
check_volumes_outside_state(build_volume, nullptr);
|
||||
m_requires_check_outside_state = false;
|
||||
}
|
||||
}
|
||||
@ -6032,15 +6230,20 @@ void GLCanvas3D::_render_selection()
|
||||
|
||||
void GLCanvas3D::_render_sequential_clearance()
|
||||
{
|
||||
if (m_layers_editing.is_enabled() || m_gizmos.is_dragging())
|
||||
if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects)
|
||||
return;
|
||||
|
||||
if (m_layers_editing.is_enabled())
|
||||
return;
|
||||
|
||||
switch (m_gizmos.get_current_type())
|
||||
{
|
||||
case GLGizmosManager::EType::Flatten:
|
||||
case GLGizmosManager::EType::Cut:
|
||||
case GLGizmosManager::EType::Hollow:
|
||||
case GLGizmosManager::EType::SlaSupports:
|
||||
case GLGizmosManager::EType::MmuSegmentation:
|
||||
case GLGizmosManager::EType::Measure:
|
||||
case GLGizmosManager::EType::Emboss:
|
||||
case GLGizmosManager::EType::Simplify:
|
||||
case GLGizmosManager::EType::FdmSupports:
|
||||
case GLGizmosManager::EType::Seam: { return; }
|
||||
default: { break; }
|
||||
|
@ -619,23 +619,35 @@ public:
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ContoursList
|
||||
{
|
||||
// list of unique contours
|
||||
Polygons contours;
|
||||
// if defined: list of transforms to apply to contours
|
||||
std::optional<std::vector<std::pair<size_t, Transform3d>>> trafos;
|
||||
|
||||
bool empty() const { return contours.empty(); }
|
||||
};
|
||||
|
||||
private:
|
||||
void load_arrange_settings();
|
||||
|
||||
class SequentialPrintClearance
|
||||
{
|
||||
GLModel m_fill;
|
||||
GLModel m_perimeter;
|
||||
bool m_render_fill{ true };
|
||||
bool m_visible{ false };
|
||||
// list of unique contours
|
||||
std::vector<GLModel> m_contours;
|
||||
// list of transforms used to render the contours
|
||||
std::vector<std::pair<size_t, Transform3d>> m_instances;
|
||||
bool m_evaluating{ false };
|
||||
|
||||
std::vector<Pointf3s> m_hull_2d_cache;
|
||||
std::vector<std::pair<Pointf3s, Transform3d>> m_hulls_2d_cache;
|
||||
|
||||
public:
|
||||
void set_polygons(const Polygons& polygons);
|
||||
void set_render_fill(bool render_fill) { m_render_fill = render_fill; }
|
||||
void set_visible(bool visible) { m_visible = visible; }
|
||||
void set_contours(const ContoursList& contours, bool generate_fill);
|
||||
void update_instances_trafos(const std::vector<Transform3d>& trafos);
|
||||
void render();
|
||||
bool empty() const { return m_contours.empty(); }
|
||||
|
||||
friend class GLCanvas3D;
|
||||
};
|
||||
@ -725,7 +737,10 @@ public:
|
||||
unsigned int get_volumes_count() const;
|
||||
const GLVolumeCollection& get_volumes() const { return m_volumes; }
|
||||
void reset_volumes();
|
||||
ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
|
||||
ModelInstanceEPrintVolumeState check_volumes_outside_state(bool selection_only = true) const;
|
||||
// returns true if all the volumes are completely contained in the print volume
|
||||
// returns the containment state in the given out_state, if non-null
|
||||
bool check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state, bool selection_only = true) const;
|
||||
|
||||
void init_gcode_viewer() { m_gcode_viewer.init(); }
|
||||
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }
|
||||
@ -959,24 +974,24 @@ public:
|
||||
}
|
||||
|
||||
void reset_sequential_print_clearance() {
|
||||
m_sequential_print_clearance.set_visible(false);
|
||||
m_sequential_print_clearance.set_render_fill(false);
|
||||
m_sequential_print_clearance.set_polygons(Polygons());
|
||||
m_sequential_print_clearance.m_evaluating = false;
|
||||
m_sequential_print_clearance.set_contours(ContoursList(), false);
|
||||
}
|
||||
|
||||
void set_sequential_print_clearance_visible(bool visible) {
|
||||
m_sequential_print_clearance.set_visible(visible);
|
||||
void set_sequential_print_clearance_contours(const ContoursList& contours, bool generate_fill) {
|
||||
m_sequential_print_clearance.set_contours(contours, generate_fill);
|
||||
}
|
||||
|
||||
void set_sequential_print_clearance_render_fill(bool render_fill) {
|
||||
m_sequential_print_clearance.set_render_fill(render_fill);
|
||||
bool is_sequential_print_clearance_empty() const {
|
||||
return m_sequential_print_clearance.empty();
|
||||
}
|
||||
|
||||
void set_sequential_print_clearance_polygons(const Polygons& polygons) {
|
||||
m_sequential_print_clearance.set_polygons(polygons);
|
||||
bool is_sequential_print_clearance_evaluating() const {
|
||||
return m_sequential_print_clearance.m_evaluating;
|
||||
}
|
||||
|
||||
void update_sequential_clearance();
|
||||
void update_sequential_clearance(bool force_contours_generation);
|
||||
void set_sequential_clearance_as_evaluating() { m_sequential_print_clearance.m_evaluating = true; }
|
||||
|
||||
const Print* fff_print() const;
|
||||
const SLAPrint* sla_print() const;
|
||||
|
@ -596,6 +596,38 @@ void GLModel::init_from(const indexed_triangle_set& its)
|
||||
}
|
||||
}
|
||||
|
||||
void GLModel::init_from(const Polygon& polygon, float z)
|
||||
{
|
||||
if (is_initialized()) {
|
||||
// call reset() if you want to reuse this model
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
Geometry& data = m_render_data.geometry;
|
||||
data.format = { Geometry::EPrimitiveType::Lines, Geometry::EVertexLayout::P3 };
|
||||
|
||||
const size_t segments_count = polygon.points.size();
|
||||
data.reserve_vertices(2 * segments_count);
|
||||
data.reserve_indices(2 * segments_count);
|
||||
|
||||
// vertices + indices
|
||||
unsigned int vertices_counter = 0;
|
||||
for (size_t i = 0; i < segments_count; ++i) {
|
||||
const Point& p0 = polygon.points[i];
|
||||
const Point& p1 = (i == segments_count - 1) ? polygon.points.front() : polygon.points[i + 1];
|
||||
data.add_vertex(Vec3f(unscale<float>(p0.x()), unscale<float>(p0.y()), z));
|
||||
data.add_vertex(Vec3f(unscale<float>(p1.x()), unscale<float>(p1.y()), z));
|
||||
vertices_counter += 2;
|
||||
data.add_line(vertices_counter - 2, vertices_counter - 1);
|
||||
}
|
||||
|
||||
// update bounding box
|
||||
for (size_t i = 0; i < vertices_count(); ++i) {
|
||||
m_bounding_box.merge(data.extract_position_3(i).cast<double>());
|
||||
}
|
||||
}
|
||||
|
||||
void GLModel::init_from(const Polygons& polygons, float z)
|
||||
{
|
||||
if (is_initialized()) {
|
||||
|
@ -227,6 +227,7 @@ namespace GUI {
|
||||
void init_from(const TriangleMesh& mesh);
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
void init_from(const indexed_triangle_set& its);
|
||||
void init_from(const Polygon& polygon, float z);
|
||||
void init_from(const Polygons& polygons, float z);
|
||||
bool init_from_file(const std::string& filename);
|
||||
|
||||
|
@ -926,8 +926,8 @@ void ObjectList::OnContextMenu(wxDataViewEvent& evt)
|
||||
// Do not show the context menu if the user pressed the right mouse button on the 3D scene and released it on the objects list
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
bool evt_context_menu = (canvas != nullptr) ? !canvas->is_mouse_dragging() : true;
|
||||
if (!evt_context_menu)
|
||||
canvas->mouse_up_cleanup();
|
||||
// if (!evt_context_menu)
|
||||
// canvas->mouse_up_cleanup();
|
||||
|
||||
list_manipulation(mouse_pos, evt_context_menu);
|
||||
}
|
||||
@ -4016,8 +4016,10 @@ void ObjectList::update_selections_on_canvas()
|
||||
selection.add_volumes(mode, volume_idxs, single_selection);
|
||||
}
|
||||
|
||||
wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state();
|
||||
wxGetApp().plater()->canvas3D()->render();
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
canvas->update_gizmos_on_off_state();
|
||||
canvas->check_volumes_outside_state();
|
||||
canvas->render();
|
||||
}
|
||||
|
||||
void ObjectList::select_item(const wxDataViewItem& item)
|
||||
|
@ -859,13 +859,13 @@ wxString ObjectManipulation::coordinate_type_str(ECoordinatesType type)
|
||||
#if ENABLE_OBJECT_MANIPULATION_DEBUG
|
||||
void ObjectManipulation::render_debug_window()
|
||||
{
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
// ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
||||
imgui.begin(std::string("ObjectManipulation"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize);
|
||||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Coordinates type");
|
||||
ImGui::SameLine();
|
||||
imgui.text(coordinate_type_str(m_coordinates_type));
|
||||
imgui.end();
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
// ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
||||
imgui.begin(std::string("ObjectManipulation"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize);
|
||||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Coordinates type");
|
||||
ImGui::SameLine();
|
||||
imgui.text(coordinate_type_str(m_coordinates_type));
|
||||
imgui.end();
|
||||
}
|
||||
#endif // ENABLE_OBJECT_MANIPULATION_DEBUG
|
||||
|
||||
|
@ -22,6 +22,7 @@ namespace GUI {
|
||||
static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW();
|
||||
static const ColorRGBA UPPER_PART_COLOR = ColorRGBA::CYAN();
|
||||
static const ColorRGBA LOWER_PART_COLOR = ColorRGBA::MAGENTA();
|
||||
static const ColorRGBA MODIFIER_COLOR = ColorRGBA(0.75f, 0.75f, 0.75f, 0.5f);
|
||||
|
||||
// connector colors
|
||||
static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW();
|
||||
@ -1405,7 +1406,7 @@ GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transfor
|
||||
m_parts.clear();
|
||||
for (const ModelVolume* volume : volumes) {
|
||||
assert(volume != nullptr);
|
||||
m_parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true});
|
||||
m_parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true, !volume->is_model_part()});
|
||||
m_parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f });
|
||||
m_parts.back().glmodel.init_from(volume->mesh());
|
||||
|
||||
@ -1484,13 +1485,19 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal, GLModel& sphere_mo
|
||||
const bool is_looking_forward = normal && camera.get_dir_forward().dot(*normal) < 0.05;
|
||||
|
||||
for (size_t id=0; id<m_parts.size(); ++id) {
|
||||
if (normal && (( is_looking_forward && m_parts[id].selected) ||
|
||||
(!is_looking_forward && !m_parts[id].selected) ) )
|
||||
if (!m_parts[id].is_modifier && normal && ((is_looking_forward && m_parts[id].selected) ||
|
||||
(!is_looking_forward && !m_parts[id].selected) ) )
|
||||
continue;
|
||||
const Vec3d volume_offset = model_object()->volumes[id]->get_offset();
|
||||
shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset));
|
||||
m_parts[id].glmodel.set_color(m_parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR);
|
||||
if (m_parts[id].is_modifier) {
|
||||
glsafe(::glEnable(GL_BLEND));
|
||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||
}
|
||||
m_parts[id].glmodel.set_color(m_parts[id].is_modifier ? MODIFIER_COLOR : (m_parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR));
|
||||
m_parts[id].glmodel.render();
|
||||
if (m_parts[id].is_modifier)
|
||||
glsafe(::glDisable(GL_BLEND));
|
||||
}
|
||||
|
||||
shader->stop_using();
|
||||
@ -1551,7 +1558,7 @@ bool GLGizmoCut3D::PartSelection::is_one_object() const
|
||||
if (m_parts.size() < 2)
|
||||
return true;
|
||||
return std::all_of(m_parts.begin(), m_parts.end(), [this](const Part& part) {
|
||||
return part.selected == m_parts.front().selected;
|
||||
return part.is_modifier || part.selected == m_parts.front().selected;
|
||||
});
|
||||
}
|
||||
|
||||
@ -1568,7 +1575,7 @@ void GLGizmoCut3D::PartSelection::toggle_selection(const Vec2d& mouse_pos)
|
||||
std::vector<std::pair<size_t, double>> hits_id_and_sqdist;
|
||||
|
||||
for (size_t id=0; id<m_parts.size(); ++id) {
|
||||
const Vec3d volume_offset = model_object()->volumes[id]->get_offset();
|
||||
// const Vec3d volume_offset = model_object()->volumes[id]->get_offset();
|
||||
Transform3d tr = translation_transform(model_object()->instances[m_instance_idx]->get_offset()) * translation_transform(model_object()->volumes[id]->get_offset());
|
||||
if (m_parts[id].raycaster.unproject_on_mesh(mouse_pos, tr, camera, pos, normal)) {
|
||||
hits_id_and_sqdist.emplace_back(id, (camera_pos - tr*(pos.cast<double>())).squaredNorm());
|
||||
@ -2535,29 +2542,66 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
||||
};
|
||||
|
||||
const size_t cut_parts_cnt = m_part_selection.parts().size();
|
||||
bool has_modifiers = false;
|
||||
|
||||
// Distribute SolidParts to the Upper/Lower object
|
||||
for (size_t id = 0; id < cut_parts_cnt; ++id) {
|
||||
if (ModelObject* obj = (m_part_selection.parts()[id].selected ? upper : lower))
|
||||
if (m_part_selection.parts()[id].is_modifier)
|
||||
has_modifiers = true; // modifiers will be added later to the related parts
|
||||
else if (ModelObject* obj = (m_part_selection.parts()[id].selected ? upper : lower))
|
||||
obj->add_volume(*(cut_mo->volumes[id]));
|
||||
}
|
||||
|
||||
if (has_modifiers) {
|
||||
// Distribute Modifiers to the Upper/Lower object
|
||||
auto upper_bb = upper ? upper->instance_bounding_box(instance_idx) : BoundingBoxf3();
|
||||
auto lower_bb = lower ? lower->instance_bounding_box(instance_idx) : BoundingBoxf3();
|
||||
const Transform3d inst_matrix = cut_mo->instances[instance_idx]->get_transformation().get_matrix();
|
||||
|
||||
for (size_t id = 0; id < cut_parts_cnt; ++id)
|
||||
if (m_part_selection.parts()[id].is_modifier) {
|
||||
ModelVolume* vol = cut_mo->volumes[id];
|
||||
auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix());
|
||||
// Don't add modifiers which are not intersecting with solid parts
|
||||
if (upper_bb.intersects(bb))
|
||||
upper->add_volume(*vol);
|
||||
if (lower_bb.intersects(bb))
|
||||
lower->add_volume(*vol);
|
||||
}
|
||||
}
|
||||
|
||||
ModelVolumePtrs& volumes = cut_mo->volumes;
|
||||
if (volumes.size() == cut_parts_cnt)
|
||||
if (volumes.size() == cut_parts_cnt) {
|
||||
// Means that object is cut without connectors
|
||||
|
||||
// Just add Upper and Lower objects to cut_object_ptrs and invalidate any cut information
|
||||
add_cut_objects(cut_object_ptrs, upper, lower);
|
||||
}
|
||||
else if (volumes.size() > cut_parts_cnt) {
|
||||
// Means that object is cut with connectors
|
||||
|
||||
// All volumes are distributed to Upper / Lower object,
|
||||
// So we don’t need them anymore
|
||||
for (size_t id = 0; id < cut_parts_cnt; id++)
|
||||
delete *(volumes.begin() + id);
|
||||
volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt);
|
||||
|
||||
// Perform cut just to get connectors
|
||||
const ModelObjectPtrs cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes);
|
||||
assert(create_dowels_as_separate_object ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2);
|
||||
|
||||
// Connectors from upper object
|
||||
for (const ModelVolume* volume : cut_connectors_obj[0]->volumes)
|
||||
upper->add_volume(*volume, volume->type());
|
||||
|
||||
// Connectors from lower object
|
||||
for (const ModelVolume* volume : cut_connectors_obj[1]->volumes)
|
||||
lower->add_volume(*volume, volume->type());
|
||||
|
||||
// Add Upper and Lower objects to cut_object_ptrs with saved cut information
|
||||
add_cut_objects(cut_object_ptrs, upper, lower, false);
|
||||
|
||||
// Add Dowel-connectors as separate objects to cut_object_ptrs
|
||||
if (cut_connectors_obj.size() >= 3)
|
||||
for (size_t id = 2; id < cut_connectors_obj.size(); id++)
|
||||
cut_object_ptrs.push_back(cut_connectors_obj[id]);
|
||||
@ -2567,8 +2611,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
||||
{
|
||||
for (ModelObject* mo : cut_object_ptrs) {
|
||||
TriangleMesh mesh;
|
||||
// Merge all SolidPart but not Connectors
|
||||
for (const ModelVolume* mv : mo->volumes) {
|
||||
if (mv->is_model_part()) {
|
||||
if (mv->is_model_part() && !mv->is_cut_connector()) {
|
||||
TriangleMesh m = mv->mesh();
|
||||
m.transform(mv->get_matrix());
|
||||
mesh.merge(m);
|
||||
@ -2576,9 +2621,13 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
|
||||
}
|
||||
if (! mesh.empty()) {
|
||||
ModelVolume* new_volume = mo->add_volume(mesh);
|
||||
for (int i=int(mo->volumes.size())-2; i>=0; --i)
|
||||
if (mo->volumes[i]->type() == ModelVolumeType::MODEL_PART)
|
||||
new_volume->name = mo->name;
|
||||
// Delete all merged SolidPart but not Connectors
|
||||
for (int i=int(mo->volumes.size())-2; i>=0; --i) {
|
||||
const ModelVolume* mv = mo->volumes[i];
|
||||
if (mv->is_model_part() && !mv->is_cut_connector())
|
||||
mo->delete_volume(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -149,6 +149,7 @@ class GLGizmoCut3D : public GLGizmoBase
|
||||
GLModel glmodel;
|
||||
MeshRaycaster raycaster;
|
||||
bool selected;
|
||||
bool is_modifier;
|
||||
};
|
||||
|
||||
void render(const Vec3d* normal, GLModel& sphere_model);
|
||||
|
@ -355,7 +355,7 @@ void ArrangeJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
||||
ap.apply();
|
||||
}
|
||||
|
||||
m_plater->update();
|
||||
m_plater->update((unsigned int)Plater::UpdateParams::FORCE_FULL_SCREEN_REFRESH);
|
||||
wxGetApp().obj_manipul()->set_dirty();
|
||||
|
||||
if (!m_unarranged.empty()) {
|
||||
|
@ -2218,7 +2218,6 @@ void NotificationManager::push_version_notification(NotificationType type, Notif
|
||||
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
// NoNewReleaseAvailable must not show if alfa / beta is on.
|
||||
NotificationType nttype = notification->get_type();
|
||||
if (type == NotificationType::NoNewReleaseAvailable
|
||||
&& (notification->get_type() == NotificationType::NewAlphaAvailable
|
||||
|| notification->get_type() == NotificationType::NewBetaAvailable)) {
|
||||
|
@ -1765,11 +1765,6 @@ struct Plater::priv
|
||||
void render_project_state_debug_window() const { dirty_state.render_debug_window(); }
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
|
||||
enum class UpdateParams {
|
||||
FORCE_FULL_SCREEN_REFRESH = 1,
|
||||
FORCE_BACKGROUND_PROCESSING_UPDATE = 2,
|
||||
POSTPONE_VALIDATION_ERROR_MESSAGE = 4,
|
||||
};
|
||||
void update(unsigned int flags = 0);
|
||||
void select_view(const std::string& direction);
|
||||
void select_view_3D(const std::string& name);
|
||||
@ -3055,8 +3050,10 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx)
|
||||
sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx);
|
||||
|
||||
model.delete_object(obj_idx);
|
||||
|
||||
update();
|
||||
object_list_changed();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3253,7 +3250,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
if (view3D->is_layers_editing_enabled())
|
||||
view3D->get_wxglcanvas()->Refresh();
|
||||
|
||||
if (background_process.empty())
|
||||
if (invalidated == Print::APPLY_STATUS_CHANGED || background_process.empty())
|
||||
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
||||
|
||||
if (invalidated == Print::APPLY_STATUS_INVALIDATED) {
|
||||
@ -3292,29 +3289,43 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
// or hide the old one.
|
||||
process_validation_warning(warning);
|
||||
if (printer_technology == ptFFF) {
|
||||
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
||||
view3D->get_canvas3d()->set_as_dirty();
|
||||
view3D->get_canvas3d()->request_extra_frame();
|
||||
GLCanvas3D* canvas = view3D->get_canvas3d();
|
||||
canvas->reset_sequential_print_clearance();
|
||||
canvas->set_as_dirty();
|
||||
canvas->request_extra_frame();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The print is not valid.
|
||||
// Show error as notification.
|
||||
// The print is not valid.
|
||||
// Show error as notification.
|
||||
notification_manager->push_validate_error_notification(err);
|
||||
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
|
||||
if (printer_technology == ptFFF) {
|
||||
const Print* print = background_process.fff_print();
|
||||
Polygons polygons;
|
||||
if (print->config().complete_objects)
|
||||
Print::sequential_print_horizontal_clearance_valid(*print, &polygons);
|
||||
view3D->get_canvas3d()->set_sequential_print_clearance_visible(true);
|
||||
view3D->get_canvas3d()->set_sequential_print_clearance_render_fill(true);
|
||||
view3D->get_canvas3d()->set_sequential_print_clearance_polygons(polygons);
|
||||
GLCanvas3D* canvas = view3D->get_canvas3d();
|
||||
if (canvas->is_sequential_print_clearance_empty() || canvas->is_sequential_print_clearance_evaluating()) {
|
||||
GLCanvas3D::ContoursList contours;
|
||||
contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours();
|
||||
canvas->set_sequential_print_clearance_contours(contours, true);
|
||||
canvas->set_as_dirty();
|
||||
canvas->request_extra_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (invalidated == Print::APPLY_STATUS_UNCHANGED && !background_process.empty()) {
|
||||
if (printer_technology == ptFFF) {
|
||||
// Object manipulation with gizmos may end up in a null transformation.
|
||||
// In this case, we need to trigger the completion of the sequential print clearance contours evaluation
|
||||
GLCanvas3D* canvas = view3D->get_canvas3d();
|
||||
if (canvas->is_sequential_print_clearance_evaluating()) {
|
||||
GLCanvas3D::ContoursList contours;
|
||||
contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours();
|
||||
canvas->set_sequential_print_clearance_contours(contours, true);
|
||||
canvas->set_as_dirty();
|
||||
canvas->request_extra_frame();
|
||||
}
|
||||
}
|
||||
std::string warning;
|
||||
std::string err = background_process.validate(&warning);
|
||||
if (!err.empty())
|
||||
@ -4416,7 +4427,6 @@ void Plater::priv::on_update_geometry(Vec3dsEvent<2>&)
|
||||
|
||||
void Plater::priv::on_3dcanvas_mouse_dragging_started(SimpleEvent&)
|
||||
{
|
||||
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
||||
}
|
||||
|
||||
// Update the scene from the background processing,
|
||||
@ -6005,7 +6015,7 @@ bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/*
|
||||
return true;
|
||||
}
|
||||
|
||||
void Plater::update() { p->update(); }
|
||||
void Plater::update(unsigned int flags) { p->update(flags); }
|
||||
|
||||
Worker &Plater::get_ui_job_worker() { return p->m_worker; }
|
||||
|
||||
@ -7031,7 +7041,6 @@ void Plater::force_filament_colors_update()
|
||||
if (extruders_filaments.size() > 1 &&
|
||||
p->config->option<ConfigOptionStrings>("filament_colour")->values.size() == extruders_filaments.size())
|
||||
{
|
||||
const PresetCollection& filaments = wxGetApp().preset_bundle->filaments;
|
||||
std::vector<std::string> filament_colors;
|
||||
filament_colors.reserve(extruders_filaments.size());
|
||||
|
||||
|
@ -185,7 +185,12 @@ public:
|
||||
|
||||
const wxString& get_last_loaded_gcode() const { return m_last_loaded_gcode; }
|
||||
|
||||
void update();
|
||||
enum class UpdateParams {
|
||||
FORCE_FULL_SCREEN_REFRESH = 1,
|
||||
FORCE_BACKGROUND_PROCESSING_UPDATE = 2,
|
||||
POSTPONE_VALIDATION_ERROR_MESSAGE = 4,
|
||||
};
|
||||
void update(unsigned int flags = 0);
|
||||
|
||||
// Get the worker handling the UI jobs (arrange, fill bed, etc...)
|
||||
// Here is an example of starting up an ad-hoc job:
|
||||
|
@ -1558,6 +1558,11 @@ void Selection::erase()
|
||||
wxGetApp().obj_list()->delete_from_model_and_list(items);
|
||||
ensure_not_below_bed();
|
||||
}
|
||||
|
||||
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||
canvas->set_sequential_clearance_as_evaluating();
|
||||
canvas->set_as_dirty();
|
||||
canvas->request_extra_frame();
|
||||
}
|
||||
|
||||
void Selection::render(float scale_factor)
|
||||
@ -1630,7 +1635,6 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field)
|
||||
else if (is_single_volume_or_modifier()) {
|
||||
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
|
||||
if (wxGetApp().obj_manipul()->is_local_coordinates()) {
|
||||
const GLVolume* v = (*m_volumes)[*m_list.begin()];
|
||||
orient_matrix = get_bounding_box_in_current_reference_system().second;
|
||||
orient_matrix.translation() = Vec3d::Zero();
|
||||
}
|
||||
|
@ -2983,7 +2983,7 @@ void TabPrinter::build_extruder_pages(size_t n_before_extruders)
|
||||
update();
|
||||
});
|
||||
|
||||
auto has_changes = [this, extruder_idx]() {
|
||||
auto has_changes = [this]() {
|
||||
auto dirty_options = m_presets->current_dirty_options(true);
|
||||
#if 1
|
||||
dirty_options.erase(std::remove_if(dirty_options.begin(), dirty_options.end(),
|
||||
|
@ -104,7 +104,7 @@ bool Mainsail::test(wxString& msg) const
|
||||
res = false;
|
||||
msg = format_error(body, error, status);
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned) {
|
||||
.on_complete([&](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got server/info: %2%") % name % body;
|
||||
|
||||
try {
|
||||
|
@ -49,7 +49,7 @@ using Slic3r::GUI::Config::SnapshotDB;
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
static const char *INDEX_FILENAME = "index.idx";
|
||||
//static const char *INDEX_FILENAME = "index.idx";
|
||||
static const char *TMP_EXTENSION = ".download";
|
||||
|
||||
namespace {
|
||||
|
Loading…
x
Reference in New Issue
Block a user