Merge branch 'master' into fs_emboss
This commit is contained in:
commit
9ac4b082d9
@ -29,10 +29,12 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <math.h>
|
||||
#include <ostream>
|
||||
#include <stack>
|
||||
@ -663,187 +665,176 @@ static void export_perimeters_to_svg(const std::string &path, const Polygons &co
|
||||
// find out if paths touch - at least one point of one path is within limit distance of second path
|
||||
bool paths_touch(const ExtrusionPath &path_one, const ExtrusionPath &path_two, double limit_distance)
|
||||
{
|
||||
AABBTreeLines::LinesDistancer<Line> lines_one{path_one.as_polyline().lines()};
|
||||
AABBTreeLines::LinesDistancer<Line> lines_two{path_two.as_polyline().lines()};
|
||||
|
||||
for (size_t pt_idx = 0; pt_idx < path_one.polyline.size(); pt_idx++) {
|
||||
if (lines_two.distance_from_lines<false>(path_one.polyline.points[pt_idx]) < limit_distance) { return true; }
|
||||
}
|
||||
|
||||
AABBTreeLines::LinesDistancer<Line> lines_one{path_one.as_polyline().lines()};
|
||||
for (size_t pt_idx = 0; pt_idx < path_two.polyline.size(); pt_idx++) {
|
||||
if (lines_one.distance_from_lines<false>(path_two.polyline.points[pt_idx]) < limit_distance) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ExtrusionPaths reconnect_extrusion_paths(const ExtrusionPaths& paths, double limit_distance) {
|
||||
if (paths.empty()) return paths;
|
||||
Polylines reconnect_polylines(const Polylines &polylines, double limit_distance)
|
||||
{
|
||||
if (polylines.empty())
|
||||
return polylines;
|
||||
|
||||
std::unordered_map<size_t, ExtrusionPath> connected;
|
||||
connected.reserve(paths.size());
|
||||
for (size_t i = 0; i < paths.size(); i++) {
|
||||
if (!paths[i].empty()) {
|
||||
connected.emplace(i, paths[i]);
|
||||
std::unordered_map<size_t, Polyline> connected;
|
||||
connected.reserve(polylines.size());
|
||||
for (size_t i = 0; i < polylines.size(); i++) {
|
||||
if (!polylines[i].empty()) {
|
||||
connected.emplace(i, polylines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t a = 0; a < paths.size(); a++) {
|
||||
for (size_t a = 0; a < polylines.size(); a++) {
|
||||
if (connected.find(a) == connected.end()) {
|
||||
continue;
|
||||
}
|
||||
ExtrusionPath &base = connected.at(a);
|
||||
for (size_t b = a + 1; b < paths.size(); b++) {
|
||||
Polyline &base = connected.at(a);
|
||||
for (size_t b = a + 1; b < polylines.size(); b++) {
|
||||
if (connected.find(b) == connected.end()) {
|
||||
continue;
|
||||
}
|
||||
ExtrusionPath &next = connected.at(b);
|
||||
Polyline &next = connected.at(b);
|
||||
if ((base.last_point() - next.first_point()).cast<double>().squaredNorm() < limit_distance * limit_distance) {
|
||||
base.polyline.append(std::move(next.polyline));
|
||||
base.append(std::move(next));
|
||||
connected.erase(b);
|
||||
} else if ((base.last_point() - next.last_point()).cast<double>().squaredNorm() < limit_distance * limit_distance) {
|
||||
base.polyline.points.insert(base.polyline.points.end(), next.polyline.points.rbegin(), next.polyline.points.rend());
|
||||
base.points.insert(base.points.end(), next.points.rbegin(), next.points.rend());
|
||||
connected.erase(b);
|
||||
} else if ((base.first_point() - next.last_point()).cast<double>().squaredNorm() < limit_distance * limit_distance) {
|
||||
next.polyline.append(std::move(base.polyline));
|
||||
next.append(std::move(base));
|
||||
base = std::move(next);
|
||||
base.reverse();
|
||||
connected.erase(b);
|
||||
} else if ((base.first_point() - next.first_point()).cast<double>().squaredNorm() < limit_distance * limit_distance) {
|
||||
base.reverse();
|
||||
base.polyline.append(std::move(next.polyline));
|
||||
base.append(std::move(next));
|
||||
base.reverse();
|
||||
connected.erase(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExtrusionPaths result;
|
||||
for (auto& ext : connected) {
|
||||
Polylines result;
|
||||
for (auto &ext : connected) {
|
||||
result.push_back(std::move(ext.second));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ExtrusionPaths sort_and_connect_extra_perimeters(const std::vector<ExtrusionPaths> &extra_perims, double extrusion_spacing)
|
||||
ExtrusionPaths sort_extra_perimeters(ExtrusionPaths extra_perims, int index_of_first_unanchored, double extrusion_spacing)
|
||||
{
|
||||
std::vector<ExtrusionPaths> connected_shells;
|
||||
connected_shells.reserve(extra_perims.size());
|
||||
for (const ExtrusionPaths &ps : extra_perims) {
|
||||
// this will also filter away empty paths
|
||||
connected_shells.push_back(reconnect_extrusion_paths(ps, 1.0 * extrusion_spacing));
|
||||
if (extra_perims.empty()) return {};
|
||||
|
||||
std::vector<std::unordered_set<size_t>> dependencies(extra_perims.size());
|
||||
for (size_t path_idx = 0; path_idx < extra_perims.size(); path_idx++) {
|
||||
for (size_t prev_path_idx = 0; prev_path_idx < path_idx; prev_path_idx++) {
|
||||
if (paths_touch(extra_perims[path_idx], extra_perims[prev_path_idx], extrusion_spacing * 1.5f)) {
|
||||
dependencies[path_idx].insert(prev_path_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Pidx
|
||||
{
|
||||
size_t shell;
|
||||
size_t path;
|
||||
bool operator==(const Pidx &rhs) const { return shell == rhs.shell && path == rhs.path; }
|
||||
};
|
||||
struct PidxHash
|
||||
{
|
||||
size_t operator()(const Pidx &i) const { return std::hash<size_t>{}(i.shell) ^ std::hash<size_t>{}(i.path); }
|
||||
};
|
||||
|
||||
auto get_path = [&](Pidx i) { return connected_shells[i.shell][i.path]; };
|
||||
std::vector<bool> processed(extra_perims.size(), false);
|
||||
for (size_t path_idx = 0; path_idx < index_of_first_unanchored; path_idx++) {
|
||||
processed[path_idx] = true;
|
||||
}
|
||||
|
||||
std::vector<std::unordered_map<Pidx, std::unordered_set<Pidx, PidxHash>, PidxHash>> dependencies;
|
||||
for (size_t shell = 0; shell < connected_shells.size(); shell++) {
|
||||
dependencies.push_back({});
|
||||
auto ¤t_shell = dependencies[shell];
|
||||
for (size_t path = 0; path < connected_shells[shell].size(); path++) {
|
||||
Pidx current_path{shell, path};
|
||||
std::unordered_set<Pidx, PidxHash> current_dependencies{};
|
||||
if (shell > 0) {
|
||||
for (const auto &prev_path : dependencies[shell - 1]) {
|
||||
if (paths_touch(get_path(current_path), get_path(prev_path.first), extrusion_spacing * 1.5f)) {
|
||||
current_dependencies.insert(prev_path.first);
|
||||
};
|
||||
for (size_t i = index_of_first_unanchored; i < extra_perims.size(); i++) {
|
||||
bool change = false;
|
||||
for (size_t path_idx = index_of_first_unanchored; path_idx < extra_perims.size(); path_idx++) {
|
||||
if (processed[path_idx])
|
||||
continue;
|
||||
auto processed_dep = std::find_if(dependencies[path_idx].begin(), dependencies[path_idx].end(),
|
||||
[&](size_t dep) { return processed[dep]; });
|
||||
if (processed_dep != dependencies[path_idx].end()) {
|
||||
for (auto it = dependencies[path_idx].begin(); it != dependencies[path_idx].end();) {
|
||||
if (!processed[*it]) {
|
||||
dependencies[*it].insert(path_idx);
|
||||
dependencies[path_idx].erase(it++);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
processed[path_idx] = true;
|
||||
change = true;
|
||||
}
|
||||
current_shell[current_path] = current_dependencies;
|
||||
}
|
||||
if (!change) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Point current_point{};
|
||||
for (const ExtrusionPaths &ps : connected_shells) {
|
||||
for (const ExtrusionPath &p : ps) {
|
||||
if (!p.empty()) {
|
||||
current_point = p.first_point();
|
||||
goto first_point_found;
|
||||
}
|
||||
}
|
||||
}
|
||||
first_point_found:
|
||||
Point current_point = extra_perims.begin()->first_point();
|
||||
|
||||
ExtrusionPaths sorted_paths{};
|
||||
Pidx npidx = Pidx{size_t(-1), 0};
|
||||
Pidx next_pidx = npidx;
|
||||
bool reverse = false;
|
||||
size_t null_idx = size_t(-1);
|
||||
size_t next_idx = null_idx;
|
||||
bool reverse = false;
|
||||
while (true) {
|
||||
if (next_pidx == npidx) { // find next pidx to print
|
||||
if (next_idx == null_idx) { // find next pidx to print
|
||||
double dist = std::numeric_limits<double>::max();
|
||||
for (size_t shell = 0; shell < dependencies.size(); shell++) {
|
||||
for (const auto &p : dependencies[shell]) {
|
||||
if (!p.second.empty())
|
||||
continue;
|
||||
const auto &path = get_path(p.first);
|
||||
double dist_a = (path.first_point() - current_point).cast<double>().squaredNorm();
|
||||
if (dist_a < dist) {
|
||||
dist = dist_a;
|
||||
next_pidx = p.first;
|
||||
reverse = false;
|
||||
}
|
||||
double dist_b = (path.last_point() - current_point).cast<double>().squaredNorm();
|
||||
if (dist_b < dist) {
|
||||
dist = dist_b;
|
||||
next_pidx = p.first;
|
||||
reverse = true;
|
||||
}
|
||||
for (size_t path_idx = 0; path_idx < extra_perims.size(); path_idx++) {
|
||||
if (!dependencies[path_idx].empty())
|
||||
continue;
|
||||
const auto &path = extra_perims[path_idx];
|
||||
double dist_a = (path.first_point() - current_point).cast<double>().squaredNorm();
|
||||
if (dist_a < dist) {
|
||||
dist = dist_a;
|
||||
next_idx = path_idx;
|
||||
reverse = false;
|
||||
}
|
||||
double dist_b = (path.last_point() - current_point).cast<double>().squaredNorm();
|
||||
if (dist_b < dist) {
|
||||
dist = dist_b;
|
||||
next_idx = path_idx;
|
||||
reverse = true;
|
||||
}
|
||||
}
|
||||
if (next_pidx == npidx) {
|
||||
break;
|
||||
if (next_idx == null_idx) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// we have valid next_pidx, add it to the sorted paths, update dependencies, update current point and potentialy set new next_pidx
|
||||
ExtrusionPath path = get_path(next_pidx);
|
||||
// we have valid next_idx, add it to the sorted paths, update dependencies, update current point and potentialy set new next_idx
|
||||
ExtrusionPath path = extra_perims[next_idx];
|
||||
if (reverse) {
|
||||
path.reverse();
|
||||
}
|
||||
sorted_paths.push_back(path);
|
||||
assert(dependencies[next_idx].empty());
|
||||
dependencies[next_idx].insert(null_idx);
|
||||
current_point = sorted_paths.back().last_point();
|
||||
if (next_pidx.shell < dependencies.size() - 1) {
|
||||
for (auto &p : dependencies[next_pidx.shell + 1]) {
|
||||
p.second.erase(next_pidx);
|
||||
}
|
||||
for (size_t path_idx = 0; path_idx < extra_perims.size(); path_idx++) {
|
||||
dependencies[path_idx].erase(next_idx);
|
||||
}
|
||||
dependencies[next_pidx.shell].erase(next_pidx);
|
||||
// check current and next shell for next pidx
|
||||
double dist = std::numeric_limits<double>::max();
|
||||
size_t current_shell = next_pidx.shell;
|
||||
next_pidx = npidx;
|
||||
for (size_t shell = current_shell; shell < std::min(current_shell + 2, dependencies.size()); shell++) {
|
||||
for (const auto &p : dependencies[shell]) {
|
||||
if (!p.second.empty())
|
||||
continue;
|
||||
const ExtrusionPath &next_path = get_path(p.first);
|
||||
double dist_a = (next_path.first_point() - current_point).cast<double>().squaredNorm();
|
||||
if (dist_a < dist) {
|
||||
dist = dist_a;
|
||||
next_pidx = p.first;
|
||||
reverse = false;
|
||||
}
|
||||
double dist_b = (next_path.last_point() - current_point).cast<double>().squaredNorm();
|
||||
if (dist_b < dist) {
|
||||
dist = dist_b;
|
||||
next_pidx = p.first;
|
||||
reverse = true;
|
||||
}
|
||||
double dist = std::numeric_limits<double>::max();
|
||||
next_idx = null_idx;
|
||||
|
||||
for (size_t path_idx = next_idx + 1; path_idx < extra_perims.size(); path_idx++) {
|
||||
if (!dependencies[path_idx].empty()) {
|
||||
continue;
|
||||
}
|
||||
const ExtrusionPath &next_path = extra_perims[path_idx];
|
||||
double dist_a = (next_path.first_point() - current_point).cast<double>().squaredNorm();
|
||||
if (dist_a < dist) {
|
||||
dist = dist_a;
|
||||
next_idx = path_idx;
|
||||
reverse = false;
|
||||
}
|
||||
double dist_b = (next_path.last_point() - current_point).cast<double>().squaredNorm();
|
||||
if (dist_b < dist) {
|
||||
dist = dist_b;
|
||||
next_idx = path_idx;
|
||||
reverse = true;
|
||||
}
|
||||
}
|
||||
if (dist > scaled(5.0)) {
|
||||
next_pidx = npidx;
|
||||
next_idx = null_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -875,61 +866,27 @@ first_point_found:
|
||||
// #define EXTRA_PERIM_DEBUG_FILES
|
||||
// Function will generate extra perimeters clipped over nonbridgeable areas of the provided surface and returns both the new perimeters and
|
||||
// Polygons filled by those clipped perimeters
|
||||
std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over_overhangs(ExPolygons infill_area,
|
||||
const Polygons &lower_slices_polygons,
|
||||
const Flow &overhang_flow,
|
||||
double scaled_resolution,
|
||||
std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over_overhangs(ExPolygons infill_area,
|
||||
const Polygons &lower_slices_polygons,
|
||||
int perimeter_count,
|
||||
const Flow &overhang_flow,
|
||||
double scaled_resolution,
|
||||
const PrintObjectConfig &object_config,
|
||||
const PrintConfig &print_config)
|
||||
{
|
||||
coord_t anchors_size = scale_(EXTERNAL_INFILL_MARGIN);
|
||||
coord_t anchors_size = std::min(coord_t(scale_(EXTERNAL_INFILL_MARGIN)), overhang_flow.scaled_spacing() * (perimeter_count + 1));
|
||||
|
||||
BoundingBox infill_area_bb = get_extents(infill_area).inflated(SCALED_EPSILON);
|
||||
Polygons optimized_lower_slices = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_polygons, infill_area_bb);
|
||||
Polygons overhangs = diff(infill_area, optimized_lower_slices);
|
||||
|
||||
if (overhangs.empty()) { return {}; }
|
||||
Polygons anchors = intersection(infill_area, optimized_lower_slices);
|
||||
Polygons inset_anchors; // anchored area inset by the anchor length
|
||||
{
|
||||
std::vector<double> deltas{anchors_size * 0.15 + 0.5 * overhang_flow.scaled_spacing(),
|
||||
anchors_size * 0.33 + 0.5 * overhang_flow.scaled_spacing(),
|
||||
anchors_size * 0.66 + 0.5 * overhang_flow.scaled_spacing(), anchors_size * 1.00};
|
||||
|
||||
std::vector<Polygons> anchor_areas_w_delta_anchor_size{};
|
||||
for (double delta : deltas) {
|
||||
// for each delta, store anchors without the delta region around overhangs
|
||||
anchor_areas_w_delta_anchor_size.push_back(diff(anchors, expand(overhangs, delta, EXTRA_PERIMETER_OFFSET_PARAMETERS)));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size() - 1; i++) {
|
||||
// Then, clip off each anchor area by the next area expanded back to original size, so that this smaller anchor region is only where larger wouldnt fit
|
||||
anchor_areas_w_delta_anchor_size[i] = diff(anchor_areas_w_delta_anchor_size[i], expand(anchor_areas_w_delta_anchor_size[i + 1],
|
||||
deltas[i + 1], EXTRA_PERIMETER_OFFSET_PARAMETERS));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size(); i++) {
|
||||
inset_anchors = union_(inset_anchors, anchor_areas_w_delta_anchor_size[i]);
|
||||
}
|
||||
|
||||
inset_anchors = expand(inset_anchors, 0.1*overhang_flow.scaled_width());
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
{
|
||||
std::vector<std::string> colors = {"blue", "purple", "orange", "red"};
|
||||
BoundingBox bbox = get_extents(anchors);
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG svg(debug_out_path("anchored").c_str(), bbox);
|
||||
for (const Line &line : to_lines(inset_anchors)) svg.draw(line, "green", scale_(0.2));
|
||||
for (size_t i = 0; i < anchor_areas_w_delta_anchor_size.size(); i++) {
|
||||
for (const Line &line : to_lines(anchor_areas_w_delta_anchor_size[i])) svg.draw(line, colors[i], scale_(0.1));
|
||||
}
|
||||
svg.Close();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Polygons inset_overhang_area = diff(infill_area, inset_anchors);
|
||||
AABBTreeLines::LinesDistancer<Line> lower_layer_aabb_tree{to_lines(optimized_lower_slices)};
|
||||
Polygons anchors = intersection(infill_area, optimized_lower_slices);
|
||||
Polygons inset_anchors = diff(anchors,
|
||||
expand(overhangs, anchors_size + 0.1 * overhang_flow.scaled_width(), EXTRA_PERIMETER_OFFSET_PARAMETERS));
|
||||
Polygons inset_overhang_area = diff(infill_area, inset_anchors);
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
{
|
||||
@ -944,7 +901,7 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
|
||||
Polygons inset_overhang_area_left_unfilled;
|
||||
|
||||
std::vector<std::vector<ExtrusionPaths>> extra_perims; // overhang region -> shell -> shell parts
|
||||
std::vector<ExtrusionPaths> extra_perims; // overhang region -> extrusion paths
|
||||
for (const ExPolygon &overhang : union_ex(to_expolygons(inset_overhang_area))) {
|
||||
Polygons overhang_to_cover = to_polygons(overhang);
|
||||
Polygons expanded_overhang_to_cover = expand(overhang_to_cover, 1.1 * overhang_flow.scaled_spacing());
|
||||
@ -956,9 +913,7 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
overhang_to_cover.end());
|
||||
continue;
|
||||
}
|
||||
|
||||
extra_perims.emplace_back();
|
||||
std::vector<ExtrusionPaths> &overhang_region = extra_perims.back();
|
||||
ExtrusionPaths &overhang_region = extra_perims.emplace_back();
|
||||
|
||||
Polygons anchoring = intersection(expanded_overhang_to_cover, inset_anchors);
|
||||
Polygons perimeter_polygon = offset(union_(expand(overhang_to_cover, 0.1 * overhang_flow.scaled_spacing()), anchoring),
|
||||
@ -1004,9 +959,9 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
if (perimeter_polygon.empty()) { // fill possible gaps of single extrusion width
|
||||
Polygons shrinked = intersection(offset(prev, -0.3 * overhang_flow.scaled_spacing()), expanded_overhang_to_cover);
|
||||
if (!shrinked.empty()) {
|
||||
overhang_region.emplace_back();
|
||||
extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(),
|
||||
overhang_flow.width(), overhang_flow.height());
|
||||
extrusion_paths_append(overhang_region, reconnect_polylines(perimeter, overhang_flow.scaled_spacing()),
|
||||
ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(),
|
||||
overhang_flow.height());
|
||||
}
|
||||
|
||||
Polylines fills;
|
||||
@ -1017,15 +972,15 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
}
|
||||
if (!fills.empty()) {
|
||||
fills = intersection_pl(fills, shrinked_overhang_to_cover);
|
||||
overhang_region.emplace_back();
|
||||
extrusion_paths_append(overhang_region.back(), fills, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(),
|
||||
overhang_flow.width(), overhang_flow.height());
|
||||
extrusion_paths_append(overhang_region, reconnect_polylines(fills, overhang_flow.scaled_spacing()),
|
||||
ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(),
|
||||
overhang_flow.height());
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
overhang_region.emplace_back();
|
||||
extrusion_paths_append(overhang_region.back(), perimeter, ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(),
|
||||
overhang_flow.width(), overhang_flow.height());
|
||||
extrusion_paths_append(overhang_region, reconnect_polylines(perimeter, overhang_flow.scaled_spacing()),
|
||||
ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(),
|
||||
overhang_flow.height());
|
||||
}
|
||||
|
||||
if (intersection(perimeter_polygon, real_overhang).empty()) { continuation_loops--; }
|
||||
@ -1059,16 +1014,23 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
for (const Line &line : to_lines(inset_overhang_area_left_unfilled)) svg.draw(line, "red", scale_(0.05));
|
||||
svg.Close();
|
||||
#endif
|
||||
overhang_region.erase(std::remove_if(overhang_region.begin(), overhang_region.end(),
|
||||
[](const ExtrusionPath &p) { return p.empty(); }),
|
||||
overhang_region.end());
|
||||
|
||||
std::reverse(overhang_region.begin(), overhang_region.end()); // reverse the order, It shall be printed from inside out
|
||||
if (!overhang_region.empty()) {
|
||||
auto is_anchored = [&lower_layer_aabb_tree](const ExtrusionPath &path) {
|
||||
return lower_layer_aabb_tree.distance_from_lines<true>(path.first_point()) <= 0 ||
|
||||
lower_layer_aabb_tree.distance_from_lines<true>(path.last_point()) <= 0;
|
||||
};
|
||||
std::reverse(overhang_region.begin(), overhang_region.end());
|
||||
auto first_unanchored = std::stable_partition(overhang_region.begin(), overhang_region.end(), is_anchored);
|
||||
int index_of_first_unanchored = first_unanchored - overhang_region.begin();
|
||||
overhang_region = sort_extra_perimeters(overhang_region, index_of_first_unanchored, overhang_flow.scaled_spacing());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ExtrusionPaths> result{};
|
||||
for (const std::vector<ExtrusionPaths> &paths : extra_perims) {
|
||||
result.push_back(sort_and_connect_extra_perimeters(paths, overhang_flow.scaled_spacing()));
|
||||
}
|
||||
|
||||
#ifdef EXTRA_PERIM_DEBUG_FILES
|
||||
BoundingBox bbox = get_extents(inset_overhang_area);
|
||||
bbox.offset(scale_(2.));
|
||||
@ -1081,7 +1043,7 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
|
||||
inset_overhang_area_left_unfilled = union_(inset_overhang_area_left_unfilled);
|
||||
|
||||
return {result, diff(inset_overhang_area, inset_overhang_area_left_unfilled)};
|
||||
return {extra_perims, diff(inset_overhang_area, inset_overhang_area_left_unfilled)};
|
||||
}
|
||||
|
||||
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
|
||||
@ -1315,6 +1277,7 @@ void PerimeterGenerator::process_arachne(
|
||||
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
||||
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
|
||||
lower_slices_polygons_cache,
|
||||
loop_number + 1,
|
||||
params.overhang_flow, params.scaled_resolution,
|
||||
params.object_config, params.print_config);
|
||||
if (!extra_perimeters.empty()) {
|
||||
@ -1609,6 +1572,7 @@ void PerimeterGenerator::process_classic(
|
||||
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
||||
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas,
|
||||
lower_slices_polygons_cache,
|
||||
loop_number + 1,
|
||||
params.overhang_flow, params.scaled_resolution,
|
||||
params.object_config, params.print_config);
|
||||
if (!extra_perimeters.empty()) {
|
||||
|
@ -511,202 +511,38 @@ int GLVolumeCollection::load_wipe_tower_preview(
|
||||
|
||||
// We'll now create the box with jagged edge. y-coordinates of the pre-generated model
|
||||
// are shifted so that the front edge has y=0 and centerline of the back edge has y=depth:
|
||||
// We split the box in three main pieces,
|
||||
// the two laterals are identical and the central is the one containing the jagged geometry
|
||||
|
||||
// lateral parts generator
|
||||
auto generate_lateral = [&](float min_x, float max_x) {
|
||||
const std::vector<Vec3f> vertices = {
|
||||
{ min_x, -(depth + brim_width), 0.0f },
|
||||
{ max_x, -(depth + brim_width), 0.0f },
|
||||
{ min_x, -(depth + brim_width), scaled_brim_height },
|
||||
{ max_x, -(depth + brim_width), scaled_brim_height },
|
||||
{ min_x, -depth, scaled_brim_height },
|
||||
{ max_x, -depth, scaled_brim_height },
|
||||
{ min_x, -depth, 1.0f },
|
||||
{ max_x, -depth, 1.0f },
|
||||
{ min_x, 0.0f, 1.0f },
|
||||
{ max_x, 0.0f, 1.0f },
|
||||
{ min_x, 0.0f, scaled_brim_height },
|
||||
{ max_x, 0.0f, scaled_brim_height },
|
||||
{ min_x, brim_width, scaled_brim_height },
|
||||
{ max_x, brim_width, scaled_brim_height },
|
||||
{ min_x, brim_width, 0.0f },
|
||||
{ max_x, brim_width, 0.0f }
|
||||
};
|
||||
const std::vector<Vec3i> triangles = {
|
||||
{ 0, 1, 3 }, { 0, 3, 2 }, { 2, 3, 5 }, { 2, 5, 4 }, { 4, 5, 7 }, { 4, 7, 6 }, { 6, 7, 9 }, { 6, 9, 8 },
|
||||
{ 8, 9, 11 }, { 8, 11, 10 }, { 10, 11, 13 }, { 10, 13, 12 }, { 12, 13, 15 }, { 12, 15, 14 }, { 14, 15, 1 }, { 14, 1, 0 }
|
||||
};
|
||||
|
||||
indexed_triangle_set its;
|
||||
its.vertices.reserve(vertices.size());
|
||||
for (const Vec3f& v : vertices) {
|
||||
its.vertices.emplace_back(v.x(), v.y() + depth, v.z());
|
||||
}
|
||||
its.indices.reserve(triangles.size());
|
||||
for (const Vec3i& t : triangles) {
|
||||
its.indices.emplace_back(t);
|
||||
}
|
||||
return its;
|
||||
};
|
||||
|
||||
// central parts generator
|
||||
auto generate_central = [&]() {
|
||||
const std::vector<Vec3f> vertices = {
|
||||
// this part is not watertight to avoid to have different geometries for the cases
|
||||
// brim_width < 10.0
|
||||
// brim_width == 10.0
|
||||
// brim_width > 10.0
|
||||
{ 38.453f, -(depth + brim_width), 0.0f },
|
||||
{ 61.547f, -(depth + brim_width), 0.0f },
|
||||
{ 38.453f, -(depth + brim_width), scaled_brim_height },
|
||||
{ 61.547f, -(depth + brim_width), scaled_brim_height },
|
||||
{ 38.453f, -depth, scaled_brim_height },
|
||||
{ 61.547f, -depth, scaled_brim_height },
|
||||
{ 38.453f, -depth, 1.0f },
|
||||
{ 61.547f, -depth, 1.0f },
|
||||
{ 38.453f, 0.0f, 1.0f },
|
||||
{ 44.2265f, 10.0f, 1.0f },
|
||||
{ 50.0f, 0.0f, 1.0f },
|
||||
{ 55.7735f, -10.0f, 1.0f },
|
||||
{ 61.547f, 0.0f, 1.0f },
|
||||
{ 38.453f, 0.0f, scaled_brim_height },
|
||||
{ 44.2265f, 10.0f, scaled_brim_height },
|
||||
{ 50.0f, 0.0f, scaled_brim_height },
|
||||
{ 55.7735f, -10.0f, scaled_brim_height },
|
||||
{ 61.547f, 0.0f, scaled_brim_height },
|
||||
{ 38.453f, 0.0f, 0.0f },
|
||||
{ 44.2265f, 10.0f, 0.0f },
|
||||
{ 50.0f, 0.0f, 0.0f },
|
||||
{ 55.7735f, -10.0f, 0.0f },
|
||||
{ 61.547f, 0.0f, 0.0f },
|
||||
{ 38.453f, brim_width, scaled_brim_height },
|
||||
{ 61.547f, brim_width, scaled_brim_height },
|
||||
{ 38.453f, brim_width, 0.0f },
|
||||
{ 61.547f, brim_width, 0.0f },
|
||||
};
|
||||
|
||||
const std::vector<Vec3i> triangles = {
|
||||
{ 0, 1, 3 }, { 0, 3, 2 }, { 2, 3, 5 }, { 2, 5, 4 }, { 4, 5, 7 }, { 4, 7, 6 },
|
||||
{ 6, 7, 11 }, { 6, 11, 10 }, { 6, 10, 8 }, { 8, 10, 9 }, { 11, 7, 12 }, { 14, 13, 8 },
|
||||
{ 14, 8, 9 }, { 19, 18, 13 }, { 19, 13, 14 }, { 15, 14, 9 }, { 15, 9, 10 }, { 20, 19, 14 },
|
||||
{ 20, 14, 15 }, { 16, 15, 10 }, { 16, 10, 11 }, { 21, 20, 15 }, { 21, 15, 16 }, { 17, 16, 11 },
|
||||
{ 17, 11, 12 }, { 22, 21, 16 }, { 22, 16, 17 }, { 15, 16, 17 }, { 13, 15, 23 }, { 15, 17, 24 },
|
||||
{ 15, 24, 23 }, { 26, 25, 23 }, { 26, 23, 24 }, { 0, 25, 1 }, { 1, 25, 26 }, { 20, 18, 19 }
|
||||
};
|
||||
|
||||
indexed_triangle_set its;
|
||||
its.vertices.reserve(vertices.size());
|
||||
for (const Vec3f& v : vertices) {
|
||||
its.vertices.emplace_back(v.x(), v.y() + depth, v.z());
|
||||
}
|
||||
its.indices.reserve(triangles.size());
|
||||
for (const Vec3i& t : triangles) {
|
||||
its.indices.emplace_back(t);
|
||||
}
|
||||
return its;
|
||||
};
|
||||
|
||||
TriangleMesh tooth_mesh;
|
||||
indexed_triangle_set data = generate_lateral(0.0f, 38.453f);
|
||||
tooth_mesh.merge(TriangleMesh(std::move(data)));
|
||||
data = generate_central();
|
||||
tooth_mesh.merge(TriangleMesh(std::move(data)));
|
||||
data = generate_lateral(61.547f, 100.0f);
|
||||
tooth_mesh.merge(TriangleMesh(std::move(data)));
|
||||
float out_points_idx[][3] = { { 0, -depth, 0 }, { 0, 0, 0 }, { 38.453f, 0, 0 }, { 61.547f, 0, 0 }, { 100.0f, 0, 0 }, { 100.0f, -depth, 0 }, { 55.7735f, -10.0f, 0 }, { 44.2265f, 10.0f, 0 },
|
||||
{ 38.453f, 0, 1 }, { 0, 0, 1 }, { 0, -depth, 1 }, { 100.0f, -depth, 1 }, { 100.0f, 0, 1 }, { 61.547f, 0, 1 }, { 55.7735f, -10.0f, 1 }, { 44.2265f, 10.0f, 1 } };
|
||||
static constexpr const int out_facets_idx[][3] = {
|
||||
{ 0, 1, 2 }, { 3, 4, 5 }, { 6, 5, 0 }, { 3, 5, 6 }, { 6, 2, 7 }, { 6, 0, 2 }, { 8, 9, 10 }, { 11, 12, 13 }, { 10, 11, 14 }, { 14, 11, 13 }, { 15, 8, 14 },
|
||||
{ 8, 10, 14 }, { 3, 12, 4 }, { 3, 13, 12 }, { 6, 13, 3 }, { 6, 14, 13 }, { 7, 14, 6 }, { 7, 15, 14 }, { 2, 15, 7 }, { 2, 8, 15 }, { 1, 8, 2 }, { 1, 9, 8 },
|
||||
{ 0, 9, 1 }, { 0, 10, 9 }, { 5, 10, 0 }, { 5, 11, 10 }, { 4, 11, 5 }, { 4, 12, 11 } };
|
||||
indexed_triangle_set its;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
its.vertices.emplace_back(out_points_idx[i][0] / (100.f / min_width), out_points_idx[i][1] + depth, out_points_idx[i][2]);
|
||||
its.indices.reserve(28);
|
||||
for (const int* face : out_facets_idx)
|
||||
its.indices.emplace_back(face);
|
||||
TriangleMesh tooth_mesh(std::move(its));
|
||||
|
||||
// We have the mesh ready. It has one tooth and width of min_width. We will now
|
||||
// append several of these together until we are close to the required width
|
||||
// of the block. Than we can scale it precisely.
|
||||
const size_t n = std::max(1, int(width / min_width)); // How many shall be merged?
|
||||
size_t n = std::max(1, int(width / min_width)); // How many shall be merged?
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
mesh.merge(tooth_mesh);
|
||||
tooth_mesh.translate(100.0f, 0.0f, 0.0f);
|
||||
tooth_mesh.translate(min_width, 0.f, 0.f);
|
||||
}
|
||||
|
||||
// Now we add the caps along the X axis
|
||||
const float scaled_brim_width_x = brim_width * n * width / min_width;
|
||||
auto generate_negx_cap = [&]() {
|
||||
const std::vector<Vec3f> vertices = {
|
||||
{ -scaled_brim_width_x, -(depth + brim_width), 0.0f },
|
||||
{ 0.0f, -(depth + brim_width), 0.0f },
|
||||
{ -scaled_brim_width_x, -(depth + brim_width), scaled_brim_height },
|
||||
{ 0.0f, -(depth + brim_width), scaled_brim_height },
|
||||
{ 0.0f, -depth, scaled_brim_height },
|
||||
{ 0.0f, -depth, 1.0f },
|
||||
{ 0.0f, 0.0f, 1.0f },
|
||||
{ 0.0f, 0.0f, scaled_brim_height },
|
||||
{ 0.0f, brim_width, scaled_brim_height },
|
||||
{ -scaled_brim_width_x, brim_width, scaled_brim_height },
|
||||
{ 0.0f, brim_width, 0.0f },
|
||||
{ -scaled_brim_width_x, brim_width, 0.0f }
|
||||
};
|
||||
|
||||
const std::vector<Vec3i> triangles = {
|
||||
{ 0, 1, 3 }, { 0, 3, 2 }, { 2, 3, 4 }, { 2, 4, 9 }, { 9, 4, 7 }, { 9, 7, 8 }, { 9, 8, 10 }, { 9, 10, 11 },
|
||||
{ 11, 10, 1 }, { 11, 1, 0 }, { 11, 0, 2 }, { 11, 2, 9 }, { 7, 4, 5 }, { 7, 5, 6 }
|
||||
};
|
||||
|
||||
indexed_triangle_set its;
|
||||
its.vertices.reserve(vertices.size());
|
||||
for (const Vec3f& v : vertices) {
|
||||
its.vertices.emplace_back(v.x(), v.y() + depth, v.z());
|
||||
}
|
||||
its.indices.reserve(triangles.size());
|
||||
for (const Vec3i& t : triangles) {
|
||||
its.indices.emplace_back(t);
|
||||
}
|
||||
return its;
|
||||
};
|
||||
|
||||
auto generate_posx_cap = [&]() {
|
||||
const float posx_cap_x = n * 100.0f;
|
||||
const std::vector<Vec3f> vertices = {
|
||||
{ posx_cap_x, -(depth + brim_width), 0.0f },
|
||||
{ posx_cap_x + scaled_brim_width_x, -(depth + brim_width), 0.0f },
|
||||
{ posx_cap_x, -(depth + brim_width), scaled_brim_height },
|
||||
{ posx_cap_x + scaled_brim_width_x, -(depth + brim_width), scaled_brim_height },
|
||||
{ posx_cap_x, -depth, scaled_brim_height },
|
||||
{ posx_cap_x, -depth, 1.0f },
|
||||
{ posx_cap_x, 0.0f, 1.0f },
|
||||
{ posx_cap_x, 0.0f, scaled_brim_height },
|
||||
{ posx_cap_x, brim_width, scaled_brim_height },
|
||||
{ posx_cap_x + scaled_brim_width_x, brim_width, scaled_brim_height },
|
||||
{ posx_cap_x, brim_width, 0.0f },
|
||||
{ posx_cap_x + scaled_brim_width_x, brim_width, 0.0f }
|
||||
};
|
||||
|
||||
const std::vector<Vec3i> triangles = {
|
||||
{ 0, 1, 3 }, { 0, 3, 2 }, { 2, 3, 4 }, { 4, 3, 9 }, { 4, 9, 7 }, { 7, 9, 8 }, { 8, 9, 11 }, { 8, 11, 10 },
|
||||
{ 10, 11, 1 }, { 10, 1, 0 }, { 1, 11, 9 }, { 1, 9, 3 }, { 4, 7, 6 }, { 4, 6, 5 }
|
||||
};
|
||||
|
||||
indexed_triangle_set its;
|
||||
its.vertices.reserve(vertices.size());
|
||||
for (const Vec3f& v : vertices) {
|
||||
its.vertices.emplace_back(v.x(), v.y() + depth, v.z());
|
||||
}
|
||||
its.indices.reserve(triangles.size());
|
||||
for (const Vec3i& t : triangles) {
|
||||
its.indices.emplace_back(t);
|
||||
}
|
||||
return its;
|
||||
};
|
||||
|
||||
data = generate_negx_cap();
|
||||
mesh.merge(TriangleMesh(std::move(data)));
|
||||
data = generate_posx_cap();
|
||||
mesh.merge(TriangleMesh(std::move(data)));
|
||||
mesh.scale(Vec3f(width / (n * 100.0f), 1.0f, height)); // Scaling to proper width
|
||||
}
|
||||
else {
|
||||
mesh = make_cube(width, depth, height - brim_height);
|
||||
mesh.translate(0.0f, 0.0f, brim_height);
|
||||
TriangleMesh brim_mesh = make_cube(width + 2.0f * brim_width, depth + 2.0f * brim_width, brim_height);
|
||||
brim_mesh.translate(-brim_width, -brim_width, 0.0f);
|
||||
mesh.merge(brim_mesh);
|
||||
mesh.scale(Vec3f(width / (n * min_width), 1.f, height)); // Scaling to proper width
|
||||
}
|
||||
else
|
||||
mesh = make_cube(width, depth, height);
|
||||
|
||||
// We'll make another mesh to show the brim (fixed layer height):
|
||||
TriangleMesh brim_mesh = make_cube(width + 2.f * brim_width, depth + 2.f * brim_width, 0.2f);
|
||||
brim_mesh.translate(-brim_width, -brim_width, 0.f);
|
||||
mesh.merge(brim_mesh);
|
||||
|
||||
volumes.emplace_back(new GLVolume(color));
|
||||
GLVolume& v = *volumes.back();
|
||||
|
@ -113,8 +113,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
}
|
||||
}
|
||||
|
||||
auto style = config->opt_enum<SupportMaterialStyle>("support_material_style");
|
||||
|
||||
if (config->opt_bool("wipe_tower") && config->opt_bool("support_material") &&
|
||||
// Organic supports are always synchronized with object layers as of now.
|
||||
config->opt_enum<SupportMaterialStyle>("support_material_style") != smsOrganic) {
|
||||
|
@ -1937,19 +1937,19 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
|
||||
);
|
||||
|
||||
if (m_mode == EMode::FeatureSelection && m_hover_id != -1) {
|
||||
add_strings_row_to_table(*m_imgui, _u8L("Shift"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
add_strings_row_to_table(*m_imgui, "Shift", ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Enable point selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
++row_count;
|
||||
}
|
||||
|
||||
if (m_selected_features.first.feature.has_value()) {
|
||||
add_strings_row_to_table(*m_imgui, _u8L("Delete"), ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
add_strings_row_to_table(*m_imgui, "Delete", ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Restart selection"), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
++row_count;
|
||||
}
|
||||
|
||||
if (m_selected_features.first.feature.has_value() || m_selected_features.second.feature.has_value()) {
|
||||
add_row_to_table(
|
||||
[this]() {
|
||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _u8L("Esc"));
|
||||
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Esc");
|
||||
},
|
||||
[this]() {
|
||||
m_imgui->text_colored(ImGui::GetStyleColorVec4(ImGuiCol_Text), _u8L("Unselect"));
|
||||
|
@ -633,7 +633,6 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change)
|
||||
|
||||
Choice* choice = dynamic_cast<Choice*>(ht);
|
||||
choice->set_values(types);
|
||||
int dif = (int)ht->m_opt.enum_def->values().size() - (int)types.size();
|
||||
int index_in_choice = (printer_change ? std::clamp(last_in_conf - ((int)ht->m_opt.enum_def->values().size() - (int)types.size()), 0, (int)ht->m_opt.enum_def->values().size() - 1) : last_in_conf);
|
||||
choice->set_value(index_in_choice);
|
||||
if (link.supported && link.label == _(ht->m_opt.enum_def->label(index_in_choice)))
|
||||
|
@ -5646,6 +5646,7 @@ bool Plater::preview_zip_archive(const boost::filesystem::path& archive_path)
|
||||
close_zip_reader(&archive);
|
||||
throw Slic3r::FileIOError(e.what());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -188,25 +188,6 @@ static void append_enum_option( std::shared_ptr<ConfigOptionsGroup> optgroup,
|
||||
wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
|
||||
}
|
||||
|
||||
static void append_string_option(std::shared_ptr<ConfigOptionsGroup> optgroup,
|
||||
const std::string& opt_key,
|
||||
const std::string& label,
|
||||
const std::string& tooltip,
|
||||
const std::string& def_val,
|
||||
ConfigOptionMode mode = comSimple)
|
||||
{
|
||||
ConfigOptionDef def = { opt_key, coString };
|
||||
def.label = label;
|
||||
def.tooltip = tooltip;
|
||||
def.mode = mode;
|
||||
def.set_default_value(new ConfigOptionString{ def_val });
|
||||
Option option(def, opt_key);
|
||||
optgroup->append_single_option_line(option);
|
||||
|
||||
// fill data to the Search Dialog
|
||||
wxGetApp().sidebar().get_searcher().add_key(opt_key, Preset::TYPE_PREFERENCES, optgroup->config_category(), L("Preferences"));
|
||||
}
|
||||
|
||||
static void append_preferences_option_to_searcher(std::shared_ptr<ConfigOptionsGroup> optgroup,
|
||||
const std::string& opt_key,
|
||||
const wxString& label)
|
||||
|
Loading…
Reference in New Issue
Block a user