Merge branch 'main' into bugfox/layer-cooling

This commit is contained in:
SoftFever 2024-12-25 23:43:09 +08:00 committed by GitHub
commit f3b2dc26f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
778 changed files with 18275 additions and 4773 deletions

View file

@ -264,7 +264,7 @@ SplittedLine do_split_line(const ClipperZUtils::ZPath& path, const ExPolygons& c
}
for (const auto segment : node) {
for (const ClipperZUtils::ZPoint& sp : *segment) {
assert(!is_clip(sp.z()));
assert(!is_clip(sp));
result.emplace_back(to_point(sp), true, sp.z());
}
result.back().clipped = false; // Mark the end of the clipped line

View file

@ -1,128 +0,0 @@
#ifndef SRC_LIBSLIC3R_PATH_SORTING_HPP_
#define SRC_LIBSLIC3R_PATH_SORTING_HPP_
#include "AABBTreeLines.hpp"
#include "BoundingBox.hpp"
#include "Line.hpp"
#include "ankerl/unordered_dense.h"
#include <algorithm>
#include <iterator>
#include <libslic3r/Point.hpp>
#include <libslic3r/Polygon.hpp>
#include <libslic3r/ExPolygon.hpp>
#include <limits>
#include <type_traits>
#include <unordered_set>
namespace Slic3r {
namespace Algorithm {
//Sorts the paths such that all paths between begin and last_seed are printed first, in some order. The rest of the paths is sorted
// such that the paths that are touching some of the already printed are printed first, sorted secondary by the distance to the last point of the last
// printed path.
// begin, end, and last_seed are random access iterators. touch_limit_distance is used to check if the paths are touching - if any part of the path gets this close
// to the second, then they touch.
// convert_to_lines is a lambda that should accept the path as argument and return it as Lines vector, in correct order.
template<typename RandomAccessIterator, typename ToLines>
void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point start, double touch_limit_distance, ToLines convert_to_lines)
{
size_t paths_count = std::distance(begin, end);
if (paths_count <= 1)
return;
auto paths_touch = [touch_limit_distance](const AABBTreeLines::LinesDistancer<Line> &left,
const AABBTreeLines::LinesDistancer<Line> &right) {
for (const Line &l : left.get_lines()) {
if (right.distance_from_lines<false>(l.a) < touch_limit_distance) {
return true;
}
}
if (right.distance_from_lines<false>(left.get_lines().back().b) < touch_limit_distance) {
return true;
}
for (const Line &l : right.get_lines()) {
if (left.distance_from_lines<false>(l.a) < touch_limit_distance) {
return true;
}
}
if (left.distance_from_lines<false>(right.get_lines().back().b) < touch_limit_distance) {
return true;
}
return false;
};
std::vector<AABBTreeLines::LinesDistancer<Line>> distancers(paths_count);
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
distancers[path_idx] = AABBTreeLines::LinesDistancer<Line>{convert_to_lines(*std::next(begin, path_idx))};
}
std::vector<std::unordered_set<size_t>> dependencies(paths_count);
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
for (size_t next_path_idx = path_idx + 1; next_path_idx < paths_count; next_path_idx++) {
if (paths_touch(distancers[path_idx], distancers[next_path_idx])) {
dependencies[next_path_idx].insert(path_idx);
}
}
}
Point current_point = start;
std::vector<std::pair<size_t, bool>> correct_order_and_direction(paths_count);
size_t unsorted_idx = 0;
size_t null_idx = size_t(-1);
size_t next_idx = null_idx;
bool reverse = false;
while (unsorted_idx < paths_count) {
next_idx = null_idx;
double lines_dist = std::numeric_limits<double>::max();
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
if (!dependencies[path_idx].empty())
continue;
double ldist = distancers[path_idx].distance_from_lines<false>(current_point);
if (ldist < lines_dist) {
const auto &lines = distancers[path_idx].get_lines();
double dist_a = (lines.front().a - current_point).cast<double>().squaredNorm();
double dist_b = (lines.back().b - current_point).cast<double>().squaredNorm();
next_idx = path_idx;
reverse = dist_b < dist_a;
lines_dist = ldist;
}
}
// we have valid next_idx, sort it, update dependencies, update current point
correct_order_and_direction[next_idx] = {unsorted_idx, reverse};
unsorted_idx++;
current_point = reverse ? distancers[next_idx].get_lines().front().a : distancers[next_idx].get_lines().back().b;
dependencies[next_idx].insert(null_idx); // prevent it from being selected again
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
dependencies[path_idx].erase(next_idx);
}
}
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
if (correct_order_and_direction[path_idx].second) {
std::next(begin, path_idx)->reverse();
}
}
for (size_t i = 0; i < correct_order_and_direction.size() - 1; i++) {
bool swapped = false;
for (size_t j = 0; j < correct_order_and_direction.size() - i - 1; j++) {
if (correct_order_and_direction[j].first > correct_order_and_direction[j + 1].first) {
std::swap(correct_order_and_direction[j], correct_order_and_direction[j + 1]);
std::iter_swap(std::next(begin, j), std::next(begin, j + 1));
swapped = true;
}
}
if (swapped == false) {
break;
}
}
}
}} // namespace Slic3r::Algorithm
#endif /*SRC_LIBSLIC3R_PATH_SORTING_HPP_*/

View file

@ -153,10 +153,19 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
// ^ current
Point intersection_point;
bool has_intersection = Line(previous_previous.p, previous.p).intersection_infinite(Line(current.p, next.p), &intersection_point);
const auto dist_greater = [](const Point& p1, const Point& p2, const int64_t threshold) {
const auto vec = (p1 - p2).cwiseAbs().cast<uint64_t>();
if(vec.x() > threshold || vec.y() > threshold) {
// If this condition is true, the distance is definitely greater than the threshold.
// We don't need to calculate the squared norm at all, which avoid potential arithmetic overflow.
return true;
}
return vec.squaredNorm() > threshold;
};
if (!has_intersection
|| Line::distance_to_infinite_squared(intersection_point, previous.p, current.p) > double(allowed_error_distance_squared)
|| (intersection_point - previous.p).cast<int64_t>().squaredNorm() > smallest_line_segment_squared // The intersection point is way too far from the 'previous'
|| (intersection_point - current.p).cast<int64_t>().squaredNorm() > smallest_line_segment_squared) // and 'current' points, so it shouldn't replace 'current'
|| dist_greater(intersection_point, previous.p, smallest_line_segment_squared) // The intersection point is way too far from the 'previous'
|| dist_greater(intersection_point, current.p, smallest_line_segment_squared)) // and 'current' points, so it shouldn't replace 'current'
{
// We can't find a better spot for it, but the size of the line is more than 5 micron.
// So the only thing we can do here is leave it in...

View file

@ -32,7 +32,6 @@ set(lisbslic3r_sources
AABBMesh.cpp
Algorithm/LineSplit.hpp
Algorithm/LineSplit.cpp
Algorithm/PathSorting.hpp
Algorithm/RegionExpansion.hpp
Algorithm/RegionExpansion.cpp
AnyPtr.hpp
@ -504,7 +503,7 @@ if (_opts)
target_compile_options(libslic3r_cgal PRIVATE "${_opts_bad}")
endif()
target_link_libraries(libslic3r_cgal PRIVATE ${_cgal_tgt} libigl mcut)
target_link_libraries(libslic3r_cgal PRIVATE ${_cgal_tgt} libigl mcut boost_libs)
if (MSVC AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") # 32 bit MSVC workaround
target_compile_definitions(libslic3r_cgal PRIVATE CGAL_DO_NOT_USE_MPZF)
@ -580,10 +579,6 @@ target_link_libraries(libslic3r
opencv_world
)
if(NOT SLIC3R_STATIC)
target_compile_definitions(libslic3r PUBLIC BOOST_ALL_DYN_LINK)
endif()
if(NOT WIN32)
target_link_libraries(libslic3r freetype)
if (NOT APPLE)

View file

@ -67,6 +67,7 @@ public:
static const ColorRGB YELLOW() { return { 1.0f, 1.0f, 0.0f }; }
static const ColorRGB WHITE() { return { 1.0f, 1.0f, 1.0f }; }
static const ColorRGB ORCA() { return {0.0f, 150.f / 255.0f, 136.0f / 255}; }
static const ColorRGB WARNING() { return {241.0f / 255, 117.f / 255.0f, 78.0f / 255}; }
static const ColorRGB X() { return { 0.75f, 0.0f, 0.0f }; }
static const ColorRGB Y() { return { 0.0f, 0.75f, 0.0f }; }

View file

@ -130,7 +130,252 @@ struct SurfaceFill {
// Detect narrow infill regions
// Based on the anti-vibration algorithm from PrusaSlicer:
// https://github.com/prusa3d/PrusaSlicer/blob/94290e09d75f23719c3d2ab2398737c8be4c3fd6/src/libslic3r/Fill/FillEnsuring.cpp#L100-L289
// https://github.com/prusa3d/PrusaSlicer/blob/5dc04b4e8f14f65bbcc5377d62cad3e86c2aea36/src/libslic3r/Fill/FillEnsuring.cpp#L37-L273
static coord_t _MAX_LINE_LENGTH_TO_FILTER() // 4 mm.
{
return scaled<coord_t>(4.);
}
const constexpr size_t MAX_SKIPS_ALLOWED = 2; // Skip means propagation through long line.
const constexpr size_t MIN_DEPTH_FOR_LINE_REMOVING = 5;
struct LineNode
{
struct State
{
// The total number of long lines visited before this node was reached.
// We just need the minimum number of all possible paths to decide whether we can remove the line or not.
int min_skips_taken = 0;
// The total number of short lines visited before this node was reached.
int total_short_lines = 0;
// Some initial line is touching some long line. This information is propagated to neighbors.
bool initial_touches_long_lines = false;
bool initialized = false;
void reset() {
this->min_skips_taken = 0;
this->total_short_lines = 0;
this->initial_touches_long_lines = false;
this->initialized = false;
}
};
explicit LineNode(const Line &line) : line(line) {}
Line line;
// Pointers to line nodes in the previous and the next section that overlap with this line.
std::vector<LineNode*> next_section_overlapping_lines;
std::vector<LineNode*> prev_section_overlapping_lines;
bool is_removed = false;
State state;
// Return true if some initial line is touching some long line and this information was propagated into the current line.
bool is_initial_line_touching_long_lines() const {
if (prev_section_overlapping_lines.empty())
return false;
for (LineNode *line_node : prev_section_overlapping_lines) {
if (line_node->state.initial_touches_long_lines)
return true;
}
return false;
}
// Return true if the current line overlaps with some long line in the previous section.
bool is_touching_long_lines_in_previous_layer() const {
if (prev_section_overlapping_lines.empty())
return false;
const auto MAX_LINE_LENGTH_TO_FILTER = _MAX_LINE_LENGTH_TO_FILTER();
for (LineNode *line_node : prev_section_overlapping_lines) {
if (!line_node->is_removed && line_node->line.length() >= MAX_LINE_LENGTH_TO_FILTER)
return true;
}
return false;
}
// Return true if the current line overlaps with some line in the next section.
bool has_next_layer_neighbours() const {
if (next_section_overlapping_lines.empty())
return false;
for (LineNode *line_node : next_section_overlapping_lines) {
if (!line_node->is_removed)
return true;
}
return false;
}
};
using LineNodes = std::vector<LineNode>;
inline bool are_lines_overlapping_in_y_axes(const Line &first_line, const Line &second_line) {
return (second_line.a.y() <= first_line.a.y() && first_line.a.y() <= second_line.b.y())
|| (second_line.a.y() <= first_line.b.y() && first_line.b.y() <= second_line.b.y())
|| (first_line.a.y() <= second_line.a.y() && second_line.a.y() <= first_line.b.y())
|| (first_line.a.y() <= second_line.b.y() && second_line.b.y() <= first_line.b.y());
}
bool can_line_note_be_removed(const LineNode &line_node) {
const auto MAX_LINE_LENGTH_TO_FILTER = _MAX_LINE_LENGTH_TO_FILTER();
return (line_node.line.length() < MAX_LINE_LENGTH_TO_FILTER)
&& (line_node.state.total_short_lines > int(MIN_DEPTH_FOR_LINE_REMOVING)
|| (!line_node.is_initial_line_touching_long_lines() && !line_node.has_next_layer_neighbours()));
}
// Remove the node and propagate its removal to the previous sections.
void propagate_line_node_remove(const LineNode &line_node) {
std::queue<LineNode *> line_node_queue;
for (LineNode *prev_line : line_node.prev_section_overlapping_lines) {
if (prev_line->is_removed)
continue;
line_node_queue.emplace(prev_line);
}
for (; !line_node_queue.empty(); line_node_queue.pop()) {
LineNode &line_to_check = *line_node_queue.front();
if (can_line_note_be_removed(line_to_check)) {
line_to_check.is_removed = true;
for (LineNode *prev_line : line_to_check.prev_section_overlapping_lines) {
if (prev_line->is_removed)
continue;
line_node_queue.emplace(prev_line);
}
}
}
}
// Filter out short extrusions that could create vibrations.
static std::vector<Lines> filter_vibrating_extrusions(const std::vector<Lines> &lines_sections) {
// Initialize all line nodes.
std::vector<LineNodes> line_nodes_sections(lines_sections.size());
for (const Lines &lines_section : lines_sections) {
const size_t section_idx = &lines_section - lines_sections.data();
line_nodes_sections[section_idx].reserve(lines_section.size());
for (const Line &line : lines_section) {
line_nodes_sections[section_idx].emplace_back(line);
}
}
// Precalculate for each line node which line nodes in the previous and next section this line node overlaps.
for (auto curr_lines_section_it = line_nodes_sections.begin(); curr_lines_section_it != line_nodes_sections.end(); ++curr_lines_section_it) {
if (curr_lines_section_it != line_nodes_sections.begin()) {
const auto prev_lines_section_it = std::prev(curr_lines_section_it);
for (LineNode &curr_line : *curr_lines_section_it) {
for (LineNode &prev_line : *prev_lines_section_it) {
if (are_lines_overlapping_in_y_axes(curr_line.line, prev_line.line)) {
curr_line.prev_section_overlapping_lines.emplace_back(&prev_line);
}
}
}
}
if (std::next(curr_lines_section_it) != line_nodes_sections.end()) {
const auto next_lines_section_it = std::next(curr_lines_section_it);
for (LineNode &curr_line : *curr_lines_section_it) {
for (LineNode &next_line : *next_lines_section_it) {
if (are_lines_overlapping_in_y_axes(curr_line.line, next_line.line)) {
curr_line.next_section_overlapping_lines.emplace_back(&next_line);
}
}
}
}
}
const auto MAX_LINE_LENGTH_TO_FILTER = _MAX_LINE_LENGTH_TO_FILTER();
// Select each section as the initial lines section and propagate line node states from this initial lines section to the last lines section.
// During this propagation, we remove those lines that meet the conditions for its removal.
// When some line is removed, we propagate this removal to previous layers.
for (size_t initial_line_section_idx = 0; initial_line_section_idx < line_nodes_sections.size(); ++initial_line_section_idx) {
// Stars from non-removed short lines.
for (LineNode &initial_line : line_nodes_sections[initial_line_section_idx]) {
if (initial_line.is_removed || initial_line.line.length() >= MAX_LINE_LENGTH_TO_FILTER)
continue;
initial_line.state.reset();
initial_line.state.total_short_lines = 1;
initial_line.state.initial_touches_long_lines = initial_line.is_touching_long_lines_in_previous_layer();
initial_line.state.initialized = true;
}
// Iterate from the initial lines section until the last lines section.
for (size_t propagation_line_section_idx = initial_line_section_idx; propagation_line_section_idx < line_nodes_sections.size(); ++propagation_line_section_idx) {
// Before we propagate node states into next lines sections, we reset the state of all line nodes in the next line section.
if (propagation_line_section_idx + 1 < line_nodes_sections.size()) {
for (LineNode &propagation_line : line_nodes_sections[propagation_line_section_idx + 1]) {
propagation_line.state.reset();
}
}
for (LineNode &propagation_line : line_nodes_sections[propagation_line_section_idx]) {
if (propagation_line.is_removed || !propagation_line.state.initialized)
continue;
for (LineNode *neighbour_line : propagation_line.next_section_overlapping_lines) {
if (neighbour_line->is_removed)
continue;
const bool is_short_line = neighbour_line->line.length() < MAX_LINE_LENGTH_TO_FILTER;
const bool is_skip_allowed = propagation_line.state.min_skips_taken < int(MAX_SKIPS_ALLOWED);
if (!is_short_line && !is_skip_allowed)
continue;
const int neighbour_total_short_lines = propagation_line.state.total_short_lines + int(is_short_line);
const int neighbour_min_skips_taken = propagation_line.state.min_skips_taken + int(!is_short_line);
if (neighbour_line->state.initialized) {
// When the state of the node was previously filled, then we need to update data in such a way
// that will maximize the possibility of removing this node.
neighbour_line->state.min_skips_taken = std::max(neighbour_line->state.min_skips_taken, neighbour_total_short_lines);
neighbour_line->state.min_skips_taken = std::min(neighbour_line->state.min_skips_taken, neighbour_min_skips_taken);
// We will keep updating neighbor initial_touches_long_lines until it is equal to false.
if (neighbour_line->state.initial_touches_long_lines) {
neighbour_line->state.initial_touches_long_lines = propagation_line.state.initial_touches_long_lines;
}
} else {
neighbour_line->state.total_short_lines = neighbour_total_short_lines;
neighbour_line->state.min_skips_taken = neighbour_min_skips_taken;
neighbour_line->state.initial_touches_long_lines = propagation_line.state.initial_touches_long_lines;
neighbour_line->state.initialized = true;
}
}
if (can_line_note_be_removed(propagation_line)) {
// Remove the current node and propagate its removal to the previous sections.
propagation_line.is_removed = true;
propagate_line_node_remove(propagation_line);
}
}
}
}
// Create lines sections without filtered-out lines.
std::vector<Lines> lines_sections_out(line_nodes_sections.size());
for (const std::vector<LineNode> &line_nodes_section : line_nodes_sections) {
const size_t section_idx = &line_nodes_section - line_nodes_sections.data();
for (const LineNode &line_node : line_nodes_section) {
if (!line_node.is_removed) {
lines_sections_out[section_idx].emplace_back(line_node.line);
}
}
}
return lines_sections_out;
}
void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &normal_infill, ExPolygons &narrow_infill)
{
assert(fill.surface.surface_type == stInternalSolid);
@ -152,11 +397,6 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
constexpr double connect_extrusions = true;
auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) {
return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) ||
(bhigh >= alow && bhigh <= ahigh);
};
const coord_t scaled_spacing = scaled<coord_t>(fill.params.spacing);
double distance_limit_reconnection = 2.0 * double(scaled_spacing);
double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection;
@ -180,22 +420,23 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
AABBTreeLines::LinesDistancer<Line> area_walls{to_lines(inner_area)};
const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing;
std::vector<Line> vertical_lines(n_vlines);
coord_t y_min = bb.min.y();
coord_t y_max = bb.max.y();
const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing;
const coord_t y_min = bb.min.y();
const coord_t y_max = bb.max.y();
Lines vertical_lines(n_vlines);
for (size_t i = 0; i < n_vlines; i++) {
coord_t x = bb.min.x() + i * double(scaled_spacing);
vertical_lines[i].a = Point{x, y_min};
vertical_lines[i].b = Point{x, y_max};
}
if (vertical_lines.size() > 0) {
if (!vertical_lines.empty()) {
vertical_lines.push_back(vertical_lines.back());
vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min};
vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max};
}
std::vector<std::vector<Line>> polygon_sections(n_vlines);
std::vector<Lines> polygon_sections(n_vlines);
for (size_t i = 0; i < n_vlines; i++) {
const auto intersections = area_walls.intersections_with_line<true>(vertical_lines[i]);
@ -211,87 +452,7 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
}
}
struct Node
{
int section_idx;
int line_idx;
int skips_taken = 0;
bool neighbours_explored = false;
std::vector<std::pair<int, int>> neighbours{};
};
coord_t length_filter = scale_(4);
size_t skips_allowed = 2;
size_t min_removal_conut = 5;
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 < 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;
}
}
}
while (!to_visit.empty()) {
Node curr = to_visit.back();
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 > int(min_removal_conut)) ||
(curr.neighbours.empty() && !initial_touches_long_lines));
if (!is_valid_for_removal) {
for (const auto &n : curr.neighbours) {
if (to_remove.find(n) != to_remove.end()) {
is_valid_for_removal = true;
break;
}
}
}
if (!is_valid_for_removal) {
to_remove.erase({curr.section_idx, curr.line_idx});
}
to_visit.pop_back();
} 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 < 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)) {
to_visit[curr_index].neighbours.push_back({curr.section_idx + 1, lidx});
to_remove.insert({curr.section_idx + 1, lidx});
Node next_node{curr.section_idx + 1, lidx, curr.skips_taken + (nl.length() >= length_filter)};
to_visit.push_back(next_node);
}
}
}
}
}
for (const auto &pair : to_remove) {
Line &l = polygon_sections[pair.first][pair.second];
l.a = l.b;
}
}
}
}
for (size_t section_idx = 0; section_idx < polygon_sections.size(); section_idx++) {
polygon_sections[section_idx].erase(std::remove_if(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(),
[](const Line &s) { return s.a == s.b; }),
polygon_sections[section_idx].end());
std::sort(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(),
[](const Line &a, const Line &b) { return a.a.y() < b.b.y(); });
}
polygon_sections = filter_vibrating_extrusions(polygon_sections);
Polygons reconstructed_area{};
// reconstruct polygon from polygon sections

View file

@ -6813,7 +6813,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return false;
}
std::string type = (volume->type() == ModelVolumeType::MODEL_PART)?"model":"other";
// Orca#7574: always use "model" type to follow the 3MF Core Specification:
// https://github.com/3MFConsortium/spec_core/blob/20c079eef39e45ed223b8443dc9f34cbe32dc2c2/3MF%20Core%20Specification.md#3431-item-element
// > Note: items MUST NOT reference objects of type "other", either directly or recursively.
// This won't break anything because when loading the file Orca (and Bambu) simply does not care about the actual object type at all (as long as it's one of "model" & "other");
// But PrusaSlicer requires the type to be "model".
std::string type = "model";
output_buffer += " <";
output_buffer += OBJECT_TAG;

View file

@ -1792,6 +1792,7 @@ enum BambuBedType {
bbtEngineeringPlate = 2,
bbtHighTemperaturePlate = 3,
bbtTexturedPEIPlate = 4,
bbtSuperTackPlate = 5,
};
static BambuBedType to_bambu_bed_type(BedType type)
@ -1807,6 +1808,8 @@ static BambuBedType to_bambu_bed_type(BedType type)
bambu_bed_type = bbtTexturedPEIPlate;
else if (type == btPCT)
bambu_bed_type = bbtCoolPlate;
else if (type == btSuperTack)
bambu_bed_type = bbtSuperTackPlate;
return bambu_bed_type;
}
@ -1898,36 +1901,42 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
if (!print.config().small_area_infill_flow_compensation_model.empty())
m_small_area_infill_flow_compensator = make_unique<SmallAreaInfillFlowCompensator>(print.config());
file.write_format("; HEADER_BLOCK_START\n");
// Write information on the generator.
file.write_format("; generated by %s on %s\n", Slic3r::header_slic3r_generated().c_str(), Slic3r::Utils::local_timestamp().c_str());
if (is_bbl_printers)
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
//BBS: total layer number
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Total_Layer_Number_Placeholder).c_str());
m_enable_exclude_object = config().exclude_object;
//Orca: extra check for bbl printer
if (is_bbl_printers) {
if (print.calib_params().mode == CalibMode::Calib_None) { // Don't support skipping in cali mode
// list all label_object_id with sorted order here
m_enable_exclude_object = true;
m_label_objects_ids.clear();
m_label_objects_ids.reserve(print.num_object_instances());
for (const PrintObject *print_object : print.objects())
for (const PrintInstance &print_instance : print_object->instances())
m_label_objects_ids.push_back(print_instance.model_instance->get_labeled_id());
std::sort(m_label_objects_ids.begin(), m_label_objects_ids.end());
std::string objects_id_list = "; model label id: ";
for (auto it = m_label_objects_ids.begin(); it != m_label_objects_ids.end(); it++)
objects_id_list += (std::to_string(*it) + (it != m_label_objects_ids.end() - 1 ? "," : "\n"));
file.writeln(objects_id_list);
} else {
m_enable_exclude_object = false;
m_label_objects_ids.clear();
}
// Orca: Don't output Header block if BTT thumbnail is identified in the list
// Get the thumbnails value as a string
std::string thumbnails_value = print.config().option<ConfigOptionString>("thumbnails")->value;
// search string for the BTT_TFT label
bool has_BTT_thumbnail = (thumbnails_value.find("BTT_TFT") != std::string::npos);
if(!has_BTT_thumbnail){
file.write_format("; HEADER_BLOCK_START\n");
// Write information on the generator.
file.write_format("; generated by %s on %s\n", Slic3r::header_slic3r_generated().c_str(), Slic3r::Utils::local_timestamp().c_str());
if (is_bbl_printers)
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
//BBS: total layer number
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Total_Layer_Number_Placeholder).c_str());
m_enable_exclude_object = config().exclude_object;
//Orca: extra check for bbl printer
if (is_bbl_printers) {
if (print.calib_params().mode == CalibMode::Calib_None) { // Don't support skipping in cali mode
// list all label_object_id with sorted order here
m_enable_exclude_object = true;
m_label_objects_ids.clear();
m_label_objects_ids.reserve(print.num_object_instances());
for (const PrintObject *print_object : print.objects())
for (const PrintInstance &print_instance : print_object->instances())
m_label_objects_ids.push_back(print_instance.model_instance->get_labeled_id());
std::sort(m_label_objects_ids.begin(), m_label_objects_ids.end());
std::string objects_id_list = "; model label id: ";
for (auto it = m_label_objects_ids.begin(); it != m_label_objects_ids.end(); it++)
objects_id_list += (std::to_string(*it) + (it != m_label_objects_ids.end() - 1 ? "," : "\n"));
file.writeln(objects_id_list);
} else {
m_enable_exclude_object = false;
m_label_objects_ids.clear();
}
}
{
@ -1949,7 +1958,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
}
file.write_format("; HEADER_BLOCK_END\n\n");
}
// BBS: write global config at the beginning of gcode file because printer
// need these config information
@ -5981,7 +5990,7 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string
m_wipe.reset_path();*/
Point last_post_before_retract = this->last_pos();
gcode += this->retract(false, false, lift_type);
gcode += this->retract(false, false, lift_type, role);
// When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters.
// Because of it, it is necessary to call avoid crossing perimeters again with new starting point after calling retraction()
// FIXME Lukas H.: Try to predict if this second calling of avoid crossing perimeters will be needed or not. It could save computations.
@ -6183,7 +6192,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp
return true;
}
std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType lift_type)
std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType lift_type, ExtrusionRole role)
{
std::string gcode;
@ -6201,7 +6210,8 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li
(the extruder might be already retracted fully or partially). We call these
methods even if we performed wipe, since this will ensure the entire retraction
length is honored in case wipe path was too short. */
gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract();
if (role != erTopSolidInfill || EXTRUDER_CONFIG(retract_on_top_layer))
gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract();
gcode += m_writer.reset_e();
// Orca: check if should + can lift (roughly from SuperSlicer)

View file

@ -223,7 +223,7 @@ public:
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment, double z = DBL_MAX);
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift, ExtrusionRole role = erNone);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false);
bool is_BBL_Printer();

View file

@ -50,7 +50,11 @@ float get_axis_value(const std::string& line, char axis)
char match[3] = " X";
match[1] = axis;
size_t pos = line.find(match) + 2;
size_t pos = line.find(match);
if (pos == std::string::npos) {
return NAN;
}
pos += 2;
//size_t end = std::min(line.find(' ', pos + 1), line.find(';', pos + 1));
// Try to parse the numeric value.
const char* c = line.c_str();
@ -83,6 +87,15 @@ int16_t get_fan_speed(const std::string &line, GCodeFlavor flavor) {
if (flavor == (gcfMach3) || flavor == (gcfMachinekit)) {
return (int16_t)get_axis_value(line, 'P');
} else {
// Bambu machines use both M106 P1(not P0!) and M106 for part cooling fan.
// Non-bambu machines usually use M106 (without P parameter) for part cooling fan.
// P2 is reserved for auxiliary fan regardless of bambu or not.
// To keep compatibility with Bambu machines, we accept M106 and M106 P1 as the only two valid form
// of gcode that control the part cooling fan. Any other command will be ignored!
const auto idx = get_axis_value(line, 'P');
if (!isnan(idx) && idx != 1.0f) {
return -1;
}
return (int16_t)get_axis_value(line, 'S');
}
} else if (line.compare(0, 4, "M127") == 0 || line.compare(0, 4, "M107") == 0) {

View file

@ -98,6 +98,8 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags_compatible = {
const std::string GCodeProcessor::Flush_Start_Tag = " FLUSH_START";
const std::string GCodeProcessor::Flush_End_Tag = " FLUSH_END";
//Orca: External device purge tag
const std::string GCodeProcessor::External_Purge_Tag = " EXTERNAL_PURGE";
const float GCodeProcessor::Wipe_Width = 0.05f;
const float GCodeProcessor::Wipe_Height = 0.05f;
@ -2289,6 +2291,24 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
m_flushing = false;
return;
}
// Orca: Integrate filament consumption for purging performed to an external device and controlled via macros
// (eg. Happy Hare) in the filament consumption stats.
if (boost::starts_with(comment, GCodeProcessor::External_Purge_Tag)) {
std::regex numberRegex(R"(\d+\.\d+)");
std::smatch match;
std::string line(comment);
if (std::regex_search(line, match, numberRegex)) {
float filament_diameter = (static_cast<size_t>(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back();
float filament_radius = 0.5f * filament_diameter;
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
float dE = std::stof(match.str());
float volume_extruded_filament = area_filament_cross_section * dE;
m_used_filaments.update_flush_per_filament(m_extruder_id, volume_extruded_filament);
}
return;
}
if (!producers_enabled || m_producer == EProducer::OrcaSlicer) {
// height tag

View file

@ -272,6 +272,7 @@ class Print;
static const std::vector<std::string> Reserved_Tags_compatible;
static const std::string Flush_Start_Tag;
static const std::string Flush_End_Tag;
static const std::string External_Purge_Tag;
public:
enum class ETags : unsigned char
{

View file

@ -447,6 +447,12 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
// this function, fix it first.
//std::terminate();
// Orca: If moving down during below the current layer nominal Z, force XY->Z moves to avoid collisions with previous extrusions
double nominal_z = m_pos(2) - m_lifted;
if (point(2) < nominal_z - EPSILON) { // EPSILON to avoid false matches due to rounding errors
this->set_current_position_clear(false); // This forces XYZ moves to be split into XY->Z
}
/* If target Z is lower than current Z but higher than nominal Z we
don't perform the Z move but we only move in the XY plane and
adjust the nominal Z by reducing the lift amount that will be

View file

@ -717,53 +717,6 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation
return out;
}
TransformationSVD::TransformationSVD(const Transform3d& trafo)
{
const auto &m0 = trafo.matrix().block<3, 3>(0, 0);
mirror = m0.determinant() < 0.0;
Matrix3d m;
if (mirror)
m = m0 * Eigen::DiagonalMatrix<double, 3, 3>(-1.0, 1.0, 1.0);
else
m = m0;
const Eigen::JacobiSVD<Matrix3d> svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV);
u = svd.matrixU();
v = svd.matrixV();
s = svd.singularValues().asDiagonal();
scale = !s.isApprox(Matrix3d::Identity());
anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2));
rotation = !v.isApprox(u);
if (anisotropic_scale) {
rotation_90_degrees = true;
for (int i = 0; i < 3; ++i) {
const Vec3d row = v.row(i).cwiseAbs();
const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.);
const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.);
if (num_zeros != 2 || num_ones != 1) {
rotation_90_degrees = false;
break;
}
}
// Detect skew by brute force: check if the axes are still orthogonal after transformation
const Matrix3d trafo_linear = trafo.linear();
const std::array<Vec3d, 3> axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() };
std::array<Vec3d, 3> transformed_axes;
for (int i = 0; i < 3; ++i) {
transformed_axes[i] = trafo_linear * axes[i];
}
skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON ||
std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON ||
std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON;
// This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492)
// skew = ! rotation_90_degrees;
} else
skew = false;
}
// For parsing a transformation matrix from 3MF / AMF.
Transform3d transform3d_from_string(const std::string& transform_str)
{
@ -812,4 +765,78 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
return (axis.z() < 0) ? -angle : angle;
}
TransformationSVD::TransformationSVD(const Transform3d& trafo)
{
const auto &m0 = trafo.matrix().block<3, 3>(0, 0);
mirror = m0.determinant() < 0.0;
Matrix3d m;
if (mirror)
m = m0 * Eigen::DiagonalMatrix<double, 3, 3>(-1.0, 1.0, 1.0);
else
m = m0;
const Eigen::JacobiSVD<Matrix3d> svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV);
u = svd.matrixU();
v = svd.matrixV();
s = svd.singularValues().asDiagonal();
scale = !s.isApprox(Matrix3d::Identity());
anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2));
rotation = !v.isApprox(u);
if (anisotropic_scale) {
rotation_90_degrees = true;
for (int i = 0; i < 3; ++i) {
const Vec3d row = v.row(i).cwiseAbs();
const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.);
const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.);
if (num_zeros != 2 || num_ones != 1) {
rotation_90_degrees = false;
break;
}
}
// Detect skew by brute force: check if the axes are still orthogonal after transformation
const Matrix3d trafo_linear = trafo.linear();
const std::array<Vec3d, 3> axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() };
std::array<Vec3d, 3> transformed_axes;
for (int i = 0; i < 3; ++i) {
transformed_axes[i] = trafo_linear * axes[i];
}
skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON ||
std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON ||
std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON;
// This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492)
// skew = ! rotation_90_degrees;
} else
skew = false;
}
Transformation mat_around_a_point_rotate(const Transformation &InMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian)
{
auto xyz = InMat.get_offset();
Transformation left;
left.set_offset(-xyz); // at world origin
auto curMat = left * InMat;
auto qua = Eigen::Quaterniond(Eigen::AngleAxisd(rotate_theta_radian, axis));
qua.normalize();
Transform3d cur_matrix;
Transformation rotateMat4;
rotateMat4.set_matrix(cur_matrix.fromPositionOrientationScale(Vec3d(0., 0., 0.), qua, Vec3d(1., 1., 1.)));
curMat = rotateMat4 * curMat; // along_fix_axis
// rotate mat4 along fix pt
Transformation temp_world;
auto qua_world = Eigen::Quaterniond(Eigen::AngleAxisd(0, axis));
qua_world.normalize();
Transform3d cur_matrix_world;
temp_world.set_matrix(cur_matrix_world.fromPositionOrientationScale(pt, qua_world, Vec3d(1., 1., 1.)));
auto temp_xyz = temp_world.get_matrix().inverse() * xyz;
auto new_pos = temp_world.get_matrix() * (rotateMat4.get_matrix() * temp_xyz);
curMat.set_offset(new_pos);
return curMat;
}
}} // namespace Slic3r::Geometry

View file

@ -10,7 +10,7 @@
// Serialization through the Cereal library
#include <cereal/access.hpp>
namespace Slic3r {
namespace Slic3r {
namespace ClipperLib {
class PolyNode;
@ -544,6 +544,7 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
}
Transformation mat_around_a_point_rotate(const Transformation& innMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian);
} } // namespace Slicer::Geometry
#endif

View file

@ -163,12 +163,10 @@ VoronoiDiagram::detect_known_issues(const VoronoiDiagram &voronoi_diagram, Segme
return IssueType::FINITE_EDGE_WITH_NON_FINITE_VERTEX;
} else if (const IssueType cell_issue_type = detect_known_voronoi_cell_issues(voronoi_diagram, segment_begin, segment_end); cell_issue_type != IssueType::NO_ISSUE_DETECTED) {
return cell_issue_type;
} else if (!VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segment_begin, segment_end)) {
// Detection of non-planar Voronoi diagram detects at least GH issues #8474, #8514 and #8446.
return IssueType::NON_PLANAR_VORONOI_DIAGRAM;
}
// BBS: test no problem in BBS
//} else if (!VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segment_begin, segment_end)) {
// // Detection of non-planar Voronoi diagram detects at least GH issues #8474, #8514 and #8446.
// return IssueType::NON_PLANAR_VORONOI_DIAGRAM;
//}
return IssueType::NO_ISSUE_DETECTED;
}

View file

@ -13,7 +13,27 @@
namespace Slic3r {
namespace Measure {
bool get_point_projection_to_plane(const Vec3d &pt, const Vec3d &plane_origin, const Vec3d &plane_normal, Vec3d &intersection_pt)
{
auto normal = plane_normal.normalized();
auto BA = plane_origin - pt;
auto length = BA.dot(normal);
intersection_pt = pt + length * normal;
return true;
}
Vec3d get_one_point_in_plane(const Vec3d &plane_origin, const Vec3d &plane_normal)
{
Vec3d dir(1, 0, 0);
float eps = 1e-3;
if (abs(plane_normal.dot(dir)) > 1 - eps) {
dir = Vec3d(0, 1, 0);
}
auto new_pt = plane_origin + dir;
Vec3d retult;
get_point_projection_to_plane(new_pt, plane_origin, plane_normal, retult);
return retult;
}
constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it
@ -33,7 +53,7 @@ static std::tuple<Vec3d, double, double> get_center_and_radius(const std::vector
double error = std::numeric_limits<double>::max();
auto circle = Geometry::circle_ransac(out, iter, &error);
return std::make_tuple(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius, error);
}
@ -69,16 +89,18 @@ public:
bool features_extracted = false;
};
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point);
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran,bool only_select_plane);
int get_num_of_planes() const;
const std::vector<int>& get_plane_triangle_indices(int idx) const;
std::vector<int>* get_plane_tri_indices(int idx);
const std::vector<SurfaceFeature>& get_plane_features(unsigned int plane_id);
std::vector<SurfaceFeature>* get_plane_features_pointer(unsigned int plane_id);
const indexed_triangle_set& get_its() const;
private:
void update_planes();
void extract_features(int plane_idx);
std::vector<PlaneData> m_planes;
std::vector<size_t> m_face_to_plane;
indexed_triangle_set m_its;
@ -158,7 +180,7 @@ void MeasuringImpl::update_planes()
m_planes.back().normal = normal_ptr->cast<double>();
std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end());
}
// Check that each facet is part of one of the planes.
assert(std::none_of(m_face_to_plane.begin(), m_face_to_plane.end(), [](size_t val) { return val == size_t(-1); }));
@ -175,7 +197,7 @@ void MeasuringImpl::update_planes()
const auto& facets = planes[plane_id].facets;
planes[plane_id].borders.clear();
std::vector<std::array<bool, 3>> visited(facets.size(), {false, false, false});
for (int face_id=0; face_id<int(facets.size()); ++face_id) {
assert(face_to_plane[facets[face_id]] == plane_id);
@ -193,7 +215,7 @@ void MeasuringImpl::update_planes()
Halfedge_index he = sm.halfedge(Face_index(facets[face_id]));
while (he.side() != edge_id)
he = sm.next(he);
// he is the first halfedge on the border. Now walk around and append the points.
//const Halfedge_index he_orig = he;
planes[plane_id].borders.emplace_back();
@ -202,7 +224,7 @@ void MeasuringImpl::update_planes()
last_border.emplace_back(sm.point(sm.source(he)).cast<double>());
//Vertex_index target = sm.target(he);
const Halfedge_index he_start = he;
Face_index fi = he.face();
auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi));
assert(face_it != facets.end());
@ -228,7 +250,7 @@ void MeasuringImpl::update_planes()
he = sm.opposite(he);
if (he.is_invalid())
goto PLANE_FAILURE;
Face_index fi = he.face();
auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi));
if (face_it == facets.end() || *face_it != int(fi)) // This indicates a broken mesh.
@ -265,11 +287,6 @@ void MeasuringImpl::update_planes()
m_planes.shrink_to_fit();
}
void MeasuringImpl::extract_features(int plane_idx)
{
assert(! m_planes[plane_idx].features_extracted);
@ -490,7 +507,7 @@ void MeasuringImpl::extract_features(int plane_idx)
Vec3d cog = Vec3d::Zero();
size_t counter = 0;
for (const std::vector<Vec3d>& b : plane.borders) {
for (size_t i = 1; i < b.size(); ++i) {
for (size_t i = 0; i < b.size(); ++i) {
cog += b[i];
++counter;
}
@ -505,14 +522,7 @@ void MeasuringImpl::extract_features(int plane_idx)
plane.features_extracted = true;
}
std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point)
std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran,bool only_select_plane)
{
if (face_idx >= m_face_to_plane.size())
return std::optional<SurfaceFeature>();
@ -521,7 +531,7 @@ std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const
if (! plane.features_extracted)
extract_features(m_face_to_plane[face_idx]);
size_t closest_feature_idx = size_t(-1);
double min_dist = std::numeric_limits<double>::max();
@ -530,40 +540,57 @@ std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const
assert(plane.surface_features.empty() || plane.surface_features.back().get_type() == SurfaceFeatureType::Plane);
for (size_t i=0; i<plane.surface_features.size() - 1; ++i) {
// The -1 is there to prevent measuring distance to the plane itself,
// which is needless and relatively expensive.
res = get_measurement(plane.surface_features[i], point_sf);
if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented.
double dist = res.distance_strict->dist;
if (dist < feature_hover_limit && dist < min_dist) {
min_dist = std::min(dist, min_dist);
closest_feature_idx = i;
if (!only_select_plane) {
for (size_t i = 0; i < plane.surface_features.size() - 1; ++i) {
// The -1 is there to prevent measuring distance to the plane itself,
// which is needless and relatively expensive.
res = get_measurement(plane.surface_features[i], point_sf);
if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented.
double dist = res.distance_strict->dist;
if (dist < feature_hover_limit && dist < min_dist) {
min_dist = std::min(dist, min_dist);
closest_feature_idx = i;
}
}
}
}
if (closest_feature_idx != size_t(-1)) {
const SurfaceFeature& f = plane.surface_features[closest_feature_idx];
if (f.get_type() == SurfaceFeatureType::Edge) {
// If this is an edge, check if we are not close to the endpoint. If so,
// we will include the endpoint as well. Close = 10% of the lenghth of
// the edge, clamped between 0.025 and 0.5 mm.
const auto& [sp, ep] = f.get_edge();
double len_sq = (ep-sp).squaredNorm();
double limit_sq = std::max(0.025*0.025, std::min(0.5*0.5, 0.1 * 0.1 * len_sq));
if (closest_feature_idx != size_t(-1)) {
const SurfaceFeature &f = plane.surface_features[closest_feature_idx];
if (f.get_type() == SurfaceFeatureType::Edge) {
// If this is an edge, check if we are not close to the endpoint. If so,
// we will include the endpoint as well. Close = 10% of the lenghth of
// the edge, clamped between 0.025 and 0.5 mm.
const auto &[sp, ep] = f.get_edge();
double len_sq = (ep - sp).squaredNorm();
double limit_sq = std::max(0.025 * 0.025, std::min(0.5 * 0.5, 0.1 * 0.1 * len_sq));
if ((point - sp).squaredNorm() < limit_sq) {
SurfaceFeature local_f(sp);
local_f.origin_surface_feature = std::make_shared<SurfaceFeature>(local_f);
local_f.translate(world_tran);
return std::make_optional(local_f);
}
if ((point-sp).squaredNorm() < limit_sq)
return std::make_optional(SurfaceFeature(sp));
if ((point-ep).squaredNorm() < limit_sq)
return std::make_optional(SurfaceFeature(ep));
if ((point - ep).squaredNorm() < limit_sq) {
SurfaceFeature local_f(ep);
local_f.origin_surface_feature = std::make_shared<SurfaceFeature>(local_f);
local_f.translate(world_tran);
return std::make_optional(local_f);
}
}
SurfaceFeature f_tran(f);
f_tran.origin_surface_feature = std::make_shared<SurfaceFeature>(f);
f_tran.translate(world_tran);
return std::make_optional(f_tran);
}
return std::make_optional(f);
}
// Nothing detected, return the plane as a whole.
assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane);
return std::make_optional(plane.surface_features.back());
auto cur_plane = const_cast<PlaneData*>(&plane);
SurfaceFeature f_tran(cur_plane->surface_features.back());
f_tran.origin_surface_feature = std::make_shared<SurfaceFeature>(cur_plane->surface_features.back());
f_tran.translate(world_tran);
return std::make_optional(f_tran);
}
@ -583,6 +610,12 @@ const std::vector<int>& MeasuringImpl::get_plane_triangle_indices(int idx) const
return m_planes[idx].facets;
}
std::vector<int>* MeasuringImpl::get_plane_tri_indices(int idx)
{
assert(idx >= 0 && idx < int(m_planes.size()));
return &m_planes[idx].facets;
}
const std::vector<SurfaceFeature>& MeasuringImpl::get_plane_features(unsigned int plane_id)
{
assert(plane_id < m_planes.size());
@ -591,21 +624,18 @@ const std::vector<SurfaceFeature>& MeasuringImpl::get_plane_features(unsigned in
return m_planes[plane_id].surface_features;
}
std::vector<SurfaceFeature>* MeasuringImpl::get_plane_features_pointer(unsigned int plane_id) {
assert(plane_id < m_planes.size());
if (!m_planes[plane_id].features_extracted)
extract_features(plane_id);
return &m_planes[plane_id].surface_features;
}
const indexed_triangle_set& MeasuringImpl::get_its() const
{
return this->m_its;
}
Measuring::Measuring(const indexed_triangle_set& its)
: priv{std::make_unique<MeasuringImpl>(its)}
{}
@ -614,9 +644,12 @@ Measuring::~Measuring() {}
std::optional<SurfaceFeature> Measuring::get_feature(size_t face_idx, const Vec3d& point) const
std::optional<SurfaceFeature> Measuring::get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran, bool only_select_plane) const
{
return priv->get_feature(face_idx, point);
if (face_idx == 7516 || face_idx == 7517) {
std::cout << "";
}
return priv->get_feature(face_idx, point, world_tran, only_select_plane);
}
@ -796,13 +829,7 @@ static AngleAndEdges angle_plane_plane(const std::tuple<int, Vec3d, Vec3d>& p1,
return ret;
}
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring)
MeasurementResult get_measurement(const SurfaceFeature &a, const SurfaceFeature &b, bool deal_circle_result)
{
assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef);
@ -819,7 +846,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
if (f2.get_type() == SurfaceFeatureType::Point) {
Vec3d diff = (f2.get_point() - f1.get_point());
result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()});
result.distance_xyz = diff.cwiseAbs();
result.distance_xyz = diff;
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Edge) {
@ -849,13 +876,18 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
result.distance_strict = std::make_optional(DistAndPoints{ radius, c, p_on_circle });
}
else {
const Eigen::Hyperplane<double, 3> circle_plane(n, c);
const Vec3d proj = circle_plane.projection(f1.get_point());
const double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) +
(f1.get_point() - proj).squaredNorm());
if (deal_circle_result == false) {
const Eigen::Hyperplane<double, 3> circle_plane(n, c);
const Vec3d proj = circle_plane.projection(f1.get_point());
const double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm());
const Vec3d p_on_circle = c + radius * (proj - c).normalized();
result.distance_strict = std::make_optional(DistAndPoints{ dist, f1.get_point(), p_on_circle }); // TODO
const Vec3d p_on_circle = c + radius * (proj - c).normalized();
result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle});
}
else {
const double dist = (f1.get_point() - c).norm();
result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), c});
}
}
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
@ -903,31 +935,31 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
result.angle = angle_edge_edge(f1.get_edge(), f2.get_edge());
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Circle) {
const std::pair<Vec3d, Vec3d> e = f1.get_edge();
const auto& [center, radius, normal] = f2.get_circle();
const Vec3d e1e2 = (e.second - e.first);
const Vec3d e1e2_unit = e1e2.normalized();
const std::pair<Vec3d, Vec3d> e = f1.get_edge();
const auto &[center, radius, normal] = f2.get_circle();
const Vec3d e1e2 = (e.second - e.first);
const Vec3d e1e2_unit = e1e2.normalized();
std::vector<DistAndPoints> distances;
distances.emplace_back(*get_measurement(SurfaceFeature(e.first), f2).distance_strict);
distances.emplace_back(*get_measurement(SurfaceFeature(e.second), f2).distance_strict);
const Eigen::Hyperplane<double, 3> plane(e1e2_unit, center);
const Eigen::ParametrizedLine<double, 3> line = Eigen::ParametrizedLine<double, 3>::Through(e.first, e.second);
const Vec3d inter = line.intersectionPoint(plane);
const Vec3d e1inter = inter - e.first;
if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm())
distances.emplace_back(*get_measurement(SurfaceFeature(inter), f2).distance_strict);
const Eigen::Hyperplane<double, 3> plane(e1e2_unit, center);
const Eigen::ParametrizedLine<double, 3> line = Eigen::ParametrizedLine<double, 3>::Through(e.first, e.second);
const Vec3d inter = line.intersectionPoint(plane);
const Vec3d e1inter = inter - e.first;
if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) distances.emplace_back(*get_measurement(SurfaceFeature(inter), f2).distance_strict);
auto it = std::min_element(distances.begin(), distances.end(),
[](const DistAndPoints& item1, const DistAndPoints& item2) {
return item1.dist < item2.dist;
});
result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to});
auto it = std::min_element(distances.begin(), distances.end(), [](const DistAndPoints &item1, const DistAndPoints &item2) { return item1.dist < item2.dist; });
if (deal_circle_result == false) {
result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to});
}
else{
const double dist = (it->from - center).norm();
result.distance_infinite = std::make_optional(DistAndPoints{dist, it->from, center});
}
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
assert(measuring != nullptr);
const auto [from, to] = f1.get_edge();
const auto [idx, normal, origin] = f2.get_plane();
@ -944,9 +976,9 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to });
}
else {
const std::vector<SurfaceFeature>& plane_features = measuring->get_plane_features(idx);
auto plane_features = f2.world_plane_features;
std::vector<DistAndPoints> distances;
for (const SurfaceFeature& sf : plane_features) {
for (const SurfaceFeature& sf : *plane_features) {
if (sf.get_type() == SurfaceFeatureType::Edge) {
const auto m = get_measurement(sf, f1);
if (!m.distance_infinite.has_value()) {
@ -975,7 +1007,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
const auto [c0, r0, n0] = f1.get_circle();
const auto [c1, r1, n1] = f2.get_circle();
// The following code is an adaptation of the algorithm found in:
// The following code is an adaptation of the algorithm found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/DistCircle3Circle3.h
// and described in:
// https://www.geometrictools.com/Documentation/DistanceToCircle3.pdf
@ -1120,7 +1152,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
}
else {
ClosestInfo& info = candidates[0];
const double N0dD = n0.dot(D);
const Vec3d normProj = N0dD * n0;
const Vec3d compProj = D - normProj;
@ -1185,22 +1217,26 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
}
}
info.sqrDistance = distance * distance + N0dD * N0dD;
info.sqrDistance = distance * distance;
}
if (deal_circle_result == false) {
result.distance_infinite = std::make_optional(
DistAndPoints{std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest}); // TODO
} else {
const double dist = (c0 - c1).norm();
result.distance_strict = std::make_optional(DistAndPoints{dist, c0, c1});
}
result.distance_infinite = std::make_optional(DistAndPoints{ std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest }); // TODO
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
assert(measuring != nullptr);
const auto [center, radius, normal1] = f1.get_circle();
const auto [idx2, normal2, origin2] = f2.get_plane();
const bool coplanar = are_parallel(normal1, normal2) && Eigen::Hyperplane<double, 3>(normal1, center).absDistance(origin2) < EPSILON;
if (!coplanar) {
const std::vector<SurfaceFeature>& plane_features = measuring->get_plane_features(idx2);
auto plane_features = f2.world_plane_features;
std::vector<DistAndPoints> distances;
for (const SurfaceFeature& sf : plane_features) {
for (const SurfaceFeature& sf : *plane_features) {
if (sf.get_type() == SurfaceFeatureType::Edge) {
const auto m = get_measurement(sf, f1);
if (!m.distance_infinite.has_value()) {
@ -1218,6 +1254,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
});
result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to });
}
else {
const Eigen::Hyperplane<double, 3> plane(normal2, origin2);
result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(center), center, plane.projection(center)});
}
}
else {
result.distance_strict = std::make_optional(DistAndPoints{0, center, origin2});
}
}
///////////////////////////////////////////////////////////////////////////
@ -1229,23 +1272,159 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
if (are_parallel(normal1, normal2)) {
// The planes are parallel, calculate distance.
const Eigen::Hyperplane<double, 3> plane(normal1, pt1);
result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt2), pt2, plane.projection(pt2) }); // TODO
const Eigen::Hyperplane<double, 3> plane(normal2, pt2);
result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt1), pt1, plane.projection(pt1) });
}
else
result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane());
}
if (swap) {
auto swap_dist_and_points = [](DistAndPoints& dp) {
auto back = dp.to;
dp.to = dp.from;
dp.from = back;
};
if (result.distance_infinite.has_value()) {
swap_dist_and_points(*result.distance_infinite);
}
if (result.distance_strict.has_value()) {
swap_dist_and_points(*result.distance_strict);
}
}
return result;
}
bool can_set_xyz_distance(const SurfaceFeature &a, const SurfaceFeature &b) {
const bool swap = int(a.get_type()) > int(b.get_type());
const SurfaceFeature &f1 = swap ? b : a;
const SurfaceFeature &f2 = swap ? a : b;
if (f1.get_type() == SurfaceFeatureType::Point){
if (f2.get_type() == SurfaceFeatureType::Point) {
return true;
}
}
else if (f1.get_type() == SurfaceFeatureType::Circle) {
if (f2.get_type() == SurfaceFeatureType::Circle) {
return true;
}
}
return false;
}
AssemblyAction get_assembly_action(const SurfaceFeature& a, const SurfaceFeature& b)
{
AssemblyAction action;
const SurfaceFeature &f1 = a;
const SurfaceFeature &f2 = b;
if (f1.get_type() == SurfaceFeatureType::Plane) {
action.can_set_feature_1_reverse_rotation = true;
if (f2.get_type() == SurfaceFeatureType::Plane) {
const auto [idx1, normal1, pt1] = f1.get_plane();
const auto [idx2, normal2, pt2] = f2.get_plane();
action.can_set_to_center_coincidence = true;
action.can_set_feature_2_reverse_rotation = true;
if (are_parallel(normal1, normal2)) {
action.can_set_to_parallel = false;
action.has_parallel_distance = true;
action.can_around_center_of_faces = true;
Vec3d proj_pt2;
Measure::get_point_projection_to_plane(pt2, pt1, normal1, proj_pt2);
action.parallel_distance = (pt2 - proj_pt2).norm();
if ((pt2 - proj_pt2).dot(normal1) < 0) {
action.parallel_distance = -action.parallel_distance;
}
action.angle_radian = 0;
} else {
action.can_set_to_parallel = true;
action.has_parallel_distance = false;
action.can_around_center_of_faces = false;
action.parallel_distance = 0;
action.angle_radian = std::acos(std::clamp(normal2.dot(-normal1), -1.0, 1.0));
}
}
}
return action;
}
void SurfaceFeature::translate(const Vec3d& displacement) {
switch (get_type()) {
case Measure::SurfaceFeatureType::Point: {
m_pt1 = m_pt1 + displacement;
break;
}
case Measure::SurfaceFeatureType::Edge: {
m_pt1 = m_pt1 + displacement;
m_pt2 = m_pt2 + displacement;
if (m_pt3.has_value()) { //extra_point()
m_pt3 = *m_pt3 + displacement;
}
break;
}
case Measure::SurfaceFeatureType::Plane: {
//m_pt1 is normal;
m_pt2 = m_pt2 + displacement;
break;
}
case Measure::SurfaceFeatureType::Circle: {
m_pt1 = m_pt1 + displacement;
// m_pt2 is normal;
break;
}
default: break;
}
}
void SurfaceFeature::translate(const Transform3d &tran)
{
switch (get_type()) {
case Measure::SurfaceFeatureType::Point: {
m_pt1 = tran * m_pt1;
break;
}
case Measure::SurfaceFeatureType::Edge: {
m_pt1 = tran * m_pt1;
m_pt2 = tran * m_pt2;
if (m_pt3.has_value()) { // extra_point()
m_pt3 = tran * *m_pt3;
}
break;
}
case Measure::SurfaceFeatureType::Plane: {
// m_pt1 is normal;
Vec3d temp_pt1 = m_pt2 + m_pt1;
temp_pt1 = tran * temp_pt1;
m_pt2 = tran * m_pt2;
m_pt1 = (temp_pt1 - m_pt2).normalized();
break;
}
case Measure::SurfaceFeatureType::Circle: {
// m_pt1 is center;
// m_pt2 is normal;
auto local_normal = m_pt2;
auto local_center = m_pt1;
Vec3d temp_pt2 = local_normal + local_center;
temp_pt2 = tran * temp_pt2;
m_pt1 = tran * m_pt1;
auto world_center = m_pt1;
m_pt2 = (temp_pt2 - m_pt1).normalized();
} // namespace Measure
auto calc_world_radius = [&local_center, &local_normal, &tran, &world_center](const Vec3d &pt, double &value) {
Vec3d intersection_pt;
get_point_projection_to_plane(pt, local_center, local_normal, intersection_pt);
auto local_radius_pt = (intersection_pt - local_center).normalized() * value + local_center;
auto radius_pt = tran * local_radius_pt;
value = (radius_pt - world_center).norm();
};
//m_value is radius
auto new_pt = get_one_point_in_plane(local_center, local_normal);
calc_world_radius(new_pt, m_value);
break;
}
default: break;
}
}
}//namespace Measure
} // namespace Slic3r

View file

@ -6,18 +6,13 @@
#include "Point.hpp"
struct indexed_triangle_set;
namespace Slic3r {
class TriangleMesh;
namespace Measure {
enum class SurfaceFeatureType : int {
Undef = 0,
Point = 1 << 0,
@ -26,22 +21,44 @@ enum class SurfaceFeatureType : int {
Plane = 1 << 3
};
class SurfaceFeature {
bool get_point_projection_to_plane(const Vec3d &pt, const Vec3d &plane_origin, const Vec3d &plane_normal, Vec3d &intersection_pt);
Vec3d get_one_point_in_plane(const Vec3d &plane_origin, const Vec3d &plane_normal);
class SurfaceFeature
{
public:
SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional<Vec3d> pt3 = std::nullopt, double value = 0.0)
: m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {}
explicit SurfaceFeature(const Vec3d& pt)
SurfaceFeature(const Vec3d& pt)
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
SurfaceFeature(const SurfaceFeature& sf){
this->clone(sf);
volume = sf.volume;
plane_indices = sf.plane_indices;
world_tran = sf.world_tran;
world_plane_features = sf.world_plane_features;
origin_surface_feature = sf.origin_surface_feature;
}
void clone(const SurfaceFeature &sf)
{
m_type = sf.get_type();
m_pt1 = sf.get_pt1();
m_pt2 = sf.get_pt2();
m_pt3 = sf.get_pt3();
m_value = sf.get_value();
}
void translate(const Vec3d& displacement);
void translate(const Transform3d& tran);
// Get type of this feature.
SurfaceFeatureType get_type() const { return m_type; }
// For points, return the point.
Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; }
// For edges, return start and end.
std::pair<Vec3d, Vec3d> get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); }
std::pair<Vec3d, Vec3d> get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); }
// For circles, return center, radius and normal.
std::tuple<Vec3d, double, Vec3d> get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); }
@ -75,6 +92,17 @@ public:
return !operator == (other);
}
void* volume{nullptr};
std::vector<int>* plane_indices{nullptr};
Transform3d world_tran;
std::shared_ptr<std::vector<SurfaceFeature>> world_plane_features{nullptr};
std::shared_ptr<SurfaceFeature> origin_surface_feature{nullptr};
Vec3d get_pt1() const{ return m_pt1; }
Vec3d get_pt2() const { return m_pt2; }
const std::optional<Vec3d>& get_pt3() const { return m_pt3; }
double get_value() const { return m_value; }
private:
SurfaceFeatureType m_type{ SurfaceFeatureType::Undef };
Vec3d m_pt1{ Vec3d::Zero() };
@ -97,7 +125,7 @@ public:
// Given a face_idx where the mouse cursor points, return a feature that
// should be highlighted (if any).
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point) const;
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point, const Transform3d & world_tran,bool only_select_plane) const;
// Return total number of planes.
int get_num_of_planes() const;
@ -111,7 +139,7 @@ public:
// Returns the mesh used for measuring
const indexed_triangle_set& get_its() const;
private:
private:
std::unique_ptr<MeasuringImpl> priv;
};
@ -152,7 +180,24 @@ struct MeasurementResult {
};
// Returns distance/angle between two SurfaceFeatures.
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr);
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b,bool deal_circle_result =false);
bool can_set_xyz_distance(const SurfaceFeature &a, const SurfaceFeature &b);
struct AssemblyAction
{
bool can_set_to_parallel{false};
bool can_set_to_center_coincidence{false};
bool can_set_feature_1_reverse_rotation{false};
bool can_set_feature_2_reverse_rotation{false};
bool can_around_center_of_faces{false};
bool has_parallel_distance{false};
float parallel_distance;
float angle_radian{0};
Transform3d tran_for_parallel;
Transform3d tran_for_center_coincidence;
Transform3d tran_for_reverse_rotation;
};
AssemblyAction get_assembly_action(const SurfaceFeature &a, const SurfaceFeature &b);
inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); }
inline Vec3d edge_direction(const std::pair<Vec3d, Vec3d>& e) { return edge_direction(e.first, e.second); }

View file

@ -7,7 +7,7 @@ namespace Slic3r {
namespace Measure {
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h
class Polynomial1
@ -174,7 +174,7 @@ inline Polynomial1 operator * (double scalar, const Polynomial1& p)
}
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h
class RootsPolynomial
@ -242,7 +242,7 @@ public:
return false;
if (tmin >= tmax)
// Invalid ordering of interval endpoitns.
// Invalid ordering of interval endpoitns.
return false;
for (uint32_t i = 1; i <= maxIterations; ++i) {
@ -345,7 +345,7 @@ public:
}
};
// Adaptation of code found in:
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h
// Construct a single vector orthogonal to the nonzero input vector. If

View file

@ -2785,7 +2785,6 @@ std::vector<size_t> ModelVolume::get_extruders_from_multi_material_painting() co
if (!this->is_mm_painted())
return {};
assert(static_cast<size_t>(TriangleStateType::Extruder1) - 1 == 0);
const TriangleSelector::TriangleSplittingData &data = this->mmu_segmentation_facets.get_data();
std::vector<size_t> extruders;

View file

@ -1222,6 +1222,17 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
smooth_overhang_level(paths);
}
if (overhangs_reverse) {
for (const ExtrusionPath& path : paths) {
if (path.role() == erOverhangPerimeter) {
if (pg_extrusion.is_contour)
steep_overhang_contour = true;
else
steep_overhang_hole = true;
break;
}
}
}
}
}
else {
@ -3168,7 +3179,7 @@ void PerimeterGenerator::process_arachne()
// printf("Layer ID %d, Outer index %d, inner index %d, second inner index %d, maximum internal perimeter %d \n",layer_id,outer,first_internal,second_internal, max_internal);
if (outer > -1 && first_internal > -1 && second_internal > -1) { // found all three perimeters to re-order? If not the perimeters will be processed outside in.
std::vector<PerimeterGeneratorArachneExtrusion> inner_outer_extrusions; // temporary array to hold extrusions for reordering
inner_outer_extrusions.reserve(max_internal - position + 1); // reserve array containing the number of perimeters before a new island. Variables are array indexes hence need to add +1 to convert to position allocations
inner_outer_extrusions.resize(max_internal - position + 1); // reserve array containing the number of perimeters before a new island. Variables are array indexes hence need to add +1 to convert to position allocations
// printf("Allocated array size %d, max_internal index %d, start position index %d \n",max_internal-position+1,max_internal,position);
for (arr_j = max_internal; arr_j >=position; --arr_j){ // go inside out towards the external perimeter (perimeters in reverse order) and store all internal perimeters until the first one identified with inset index 2

View file

@ -823,7 +823,7 @@ static std::vector<std::string> s_Preset_filament_options {
"filament_flow_ratio", "filament_density", "filament_cost", "filament_minimal_purge_on_wipe_tower",
"nozzle_temperature", "nozzle_temperature_initial_layer",
// BBS
"cool_plate_temp", "textured_cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "textured_cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer","textured_plate_temp_initial_layer",
"cool_plate_temp", "textured_cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "textured_cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer", "textured_plate_temp_initial_layer", "supertack_plate_temp_initial_layer", "supertack_plate_temp",
// "bed_type",
//BBS:temperature_vitrification
"temperature_vitrification", "reduce_fan_stop_start_freq","dont_slow_down_outer_wall", "slow_down_for_layer_cooling", "fan_min_speed",

View file

@ -1851,6 +1851,26 @@ void PresetBundle::export_selections(AppConfig &config)
}
// BBS
void PresetBundle::set_num_filaments(unsigned int n, std::vector<std::string> new_colors) {
int old_filament_count = this->filament_presets.size();
if (n > old_filament_count && old_filament_count != 0)
filament_presets.resize(n, filament_presets.back());
else {
filament_presets.resize(n);
}
ConfigOptionStrings* filament_color = project_config.option<ConfigOptionStrings>("filament_colour");
filament_color->resize(n);
ams_multi_color_filment.resize(n);
// BBS set new filament color to new_color
if (old_filament_count < n) {
if (!new_colors.empty()) {
for (int i = old_filament_count; i < n; i++) {
filament_color->values[i] = new_colors[i - old_filament_count];
}
}
}
update_multi_material_filament_presets();
}
void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
{
int old_filament_count = this->filament_presets.size();

View file

@ -111,6 +111,7 @@ public:
void export_selections(AppConfig &config);
// BBS
void set_num_filaments(unsigned int n, std::vector<std::string> new_colors);
void set_num_filaments(unsigned int n, std::string new_col = "");
unsigned int sync_ams_list(unsigned int & unknowns);
//BBS: check whether this is the only edited filament

View file

@ -129,6 +129,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"sparse_infill_acceleration",
"internal_solid_infill_acceleration",
// BBS
"supertack_plate_temp_initial_layer",
"cool_plate_temp_initial_layer",
"textured_cool_plate_temp_initial_layer",
"eng_plate_temp_initial_layer",
@ -148,6 +149,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"retraction_minimum_travel",
"retract_before_wipe",
"retract_when_changing_layer",
"retract_on_top_layer",
"retraction_length",
"retract_length_toolchange",
"z_hop",
@ -269,6 +271,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "gcode_flavor"
|| opt_key == "single_extruder_multi_material"
|| opt_key == "nozzle_temperature"
// BBS
|| opt_key == "supertack_plate_temp"
|| opt_key == "cool_plate_temp"
|| opt_key == "textured_cool_plate_temp"
|| opt_key == "eng_plate_temp"

View file

@ -1506,7 +1506,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
num_extruders > 1 &&
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
std::array<bool, static_cast<size_t>(EnforcerBlockerType::ExtruderMax)> used_facet_states{};
std::array<bool, static_cast<size_t>(EnforcerBlockerType::ExtruderMax) + 1> used_facet_states{};
for (const ModelVolume *volume : volumes) {
const std::vector<bool> &volume_used_facet_states = volume->mmu_segmentation_facets.get_data().used_states;

View file

@ -350,6 +350,7 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(OverhangFanThreshold)
// BBS
static const t_config_enum_values s_keys_map_BedType = {
{ "Default Plate", btDefault },
{ "Supertack Plate", btSuperTack },
{ "Cool Plate", btPC },
{ "Engineering Plate", btEP },
{ "High Temp Plate", btPEI },
@ -657,6 +658,16 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
// BBS
def = this->add("supertack_plate_temp", coInts);
def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers except the initial one. "
"Value 0 means the filament does not support to print on the Cool Plate");
def->sidetext = "°C";
def->full_label = L("Bed temperature");
def->min = 0;
def->max = 120;
def->set_default_value(new ConfigOptionInts{35});
def = this->add("cool_plate_temp", coInts);
def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers except the initial one. "
@ -707,6 +718,16 @@ void PrintConfigDef::init_fff_params()
def->max = 300;
def->set_default_value(new ConfigOptionInts{45});
def = this->add("supertack_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
"Value 0 means the filament does not support to print on the Bambu Cool Plate SuperTack");
def->sidetext = "°C";
def->min = 0;
def->max = 120;
def->set_default_value(new ConfigOptionInts{ 35 });
def = this->add("cool_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
@ -762,12 +783,14 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->enum_keys_map = &s_keys_map_BedType;
// Orca: make sure the order of the values is the same as the BedType enum
def->enum_values.emplace_back("Supertack Plate");
def->enum_values.emplace_back("Cool Plate");
def->enum_values.emplace_back("Engineering Plate");
def->enum_values.emplace_back("High Temp Plate");
def->enum_values.emplace_back("Textured PEI Plate");
def->enum_values.emplace_back("Textured Cool Plate");
def->enum_labels.emplace_back(L("Smooth Cool Plate"));
def->enum_labels.emplace_back(L("Bambu Cool Plate SuperTack"));
def->enum_labels.emplace_back(L("Smooth Cool Plate / PLA Plate"));
def->enum_labels.emplace_back(L("Engineering Plate"));
def->enum_labels.emplace_back(L("Smooth High Temp Plate"));
def->enum_labels.emplace_back(L("Textured PEI Plate"));
@ -3711,6 +3734,12 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBools { false });
def = this->add("retract_on_top_layer", coBools);
def->label = L("Retract on top layer");
def->tooltip = L("Force a retraction on top layer. Disabling could prevent clog on very slow patterns with small movements, like Hilbert curve");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBools { true });
def = this->add("retraction_length", coFloats);
def->label = L("Length");
def->full_label = L("Retraction Length");
@ -3751,7 +3780,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloats { 10. });
def = this->add("z_hop", coFloats);
def->label = L("Z hop when retract");
def->label = L("Z-hop height");
def->tooltip = L("Whenever the retraction is done, the nozzle is lifted a little to create clearance between nozzle and the print. "
"It prevents nozzle from hitting the print when travel move. "
"Using spiral line to lift z can prevent stringing");
@ -3779,7 +3808,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("z_hop_types", coEnums);
def->label = L("Z hop type");
def->label = L("Z-hop type");
def->tooltip = L("Z hop type");
def->enum_keys_map = &ConfigOptionEnum<ZHopType>::get_enum_values();
def->enum_values.push_back("Auto Lift");
@ -5371,7 +5400,7 @@ void PrintConfigDef::init_fff_params()
// BBS: floats
"wipe_distance",
// bools
"retract_when_changing_layer", "wipe",
"retract_when_changing_layer", "retract_on_top_layer", "wipe",
// percents
"retract_before_wipe",
"long_retractions_when_cut",
@ -5423,7 +5452,7 @@ void PrintConfigDef::init_extruder_option_keys()
"nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset",
"retraction_length", "z_hop", "z_hop_types", "travel_slope", "retract_lift_above", "retract_lift_below", "retract_lift_enforce", "retraction_speed", "deretraction_speed",
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour",
"retract_when_changing_layer", "retract_on_top_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour",
"default_filament_profile","retraction_distances_when_cut","long_retractions_when_cut"
};
@ -5434,6 +5463,7 @@ void PrintConfigDef::init_extruder_option_keys()
"retract_lift_above",
"retract_lift_below",
"retract_lift_enforce",
"retract_on_top_layer",
"retract_restart_extra",
"retract_when_changing_layer",
"retraction_distances_when_cut",
@ -5455,7 +5485,7 @@ void PrintConfigDef::init_filament_option_keys()
"filament_diameter", "min_layer_height", "max_layer_height",
"retraction_length", "z_hop", "z_hop_types", "retract_lift_above", "retract_lift_below", "retract_lift_enforce", "retraction_speed", "deretraction_speed",
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "filament_colour",
"retract_when_changing_layer", "retract_on_top_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "filament_colour",
"default_filament_profile","retraction_distances_when_cut","long_retractions_when_cut"/*,"filament_seam_gap"*/
};
@ -5468,6 +5498,7 @@ void PrintConfigDef::init_filament_option_keys()
"retract_lift_enforce",
"retract_restart_extra",
"retract_when_changing_layer",
"retract_on_top_layer",
"retraction_distances_when_cut",
"retraction_length",
"retraction_minimum_travel",

View file

@ -254,6 +254,7 @@ enum OverhangFanThreshold {
// BBS
enum BedType {
btDefault = 0,
btSuperTack,
btPC,
btEP,
btPEI,
@ -322,6 +323,9 @@ static std::string bed_type_to_gcode_string(const BedType type)
std::string type_str;
switch (type) {
case btSuperTack:
type_str = "supertack_plate";
break;
case btPC:
type_str = "cool_plate";
break;
@ -347,6 +351,9 @@ static std::string bed_type_to_gcode_string(const BedType type)
static std::string get_bed_temp_key(const BedType type)
{
if (type == btSuperTack)
return "supertack_plate_temp";
if (type == btPC)
return "cool_plate_temp";
@ -367,6 +374,9 @@ static std::string get_bed_temp_key(const BedType type)
static std::string get_bed_temp_1st_layer_key(const BedType type)
{
if (type == btSuperTack)
return "supertack_plate_temp_initial_layer";
if (type == btPC)
return "cool_plate_temp_initial_layer";
@ -1183,9 +1193,11 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionEnum<BedType>, curr_bed_type))
((ConfigOptionInts, cool_plate_temp))
((ConfigOptionInts, textured_cool_plate_temp))
((ConfigOptionInts, supertack_plate_temp))
((ConfigOptionInts, eng_plate_temp))
((ConfigOptionInts, hot_plate_temp)) // hot is short for high temperature
((ConfigOptionInts, textured_plate_temp))
((ConfigOptionInts, supertack_plate_temp_initial_layer))
((ConfigOptionInts, cool_plate_temp_initial_layer))
((ConfigOptionInts, textured_cool_plate_temp_initial_layer))
((ConfigOptionInts, eng_plate_temp_initial_layer))
@ -1239,6 +1251,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloat, resolution))
((ConfigOptionFloats, retraction_minimum_travel))
((ConfigOptionBools, retract_when_changing_layer))
((ConfigOptionBools, retract_on_top_layer))
((ConfigOptionFloat, skirt_distance))
((ConfigOptionInt, skirt_height))
((ConfigOptionInt, skirt_loops))

View file

@ -31,6 +31,7 @@
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
#include <tbb/spin_mutex.h>
#include <Shiny/Shiny.h>
@ -2512,7 +2513,10 @@ void PrintObject::bridge_over_infill()
[](const Line &s) { return s.a == s.b; }),
polygon_sections[i].end());
std::sort(polygon_sections[i].begin(), polygon_sections[i].end(),
[](const Line &a, const Line &b) { return a.a.y() < b.b.y(); });
[](const Line &a, const Line &b) {
if (a == b) return false; // Ensure irreflexivity
return a.a.y() < b.b.y();
});
}
// reconstruct polygon from polygon sections

View file

@ -1259,14 +1259,15 @@ void PrintObject::apply_conical_overhang() {
auto upper_poly = upper_layer->merged(float(SCALED_EPSILON));
upper_poly = union_ex(upper_poly);
// Merge layer for the same reason
auto current_poly = layer->merged(float(SCALED_EPSILON));
current_poly = union_ex(current_poly);
// Avoid closing up of recessed holes in the base of a model.
// Detects when a hole is completely covered by the layer above and removes the hole from the layer above before
// adding it in.
// This should have no effect any time a hole in a layer interacts with any polygon in the layer above
if (scaled_max_hole_area > 0.0) {
// Merge layer for the same reason
auto current_poly = layer->merged(float(SCALED_EPSILON));
current_poly = union_ex(current_poly);
// Now go through all the holes in the current layer and check if they intersect anything in the layer above
// If not, then they're the top of a hole and should be cut from the layer above before the union
@ -1301,10 +1302,25 @@ void PrintObject::apply_conical_overhang() {
}
// Calculate the scaled upper poly that belongs to current region
auto p = intersection_ex(upper_layer->m_regions[region_id]->slices.surfaces, upper_poly);
// And now union it
auto p = union_ex(intersection_ex(upper_layer->m_regions[region_id]->slices.surfaces, upper_poly));
// Remove all islands that have already been fully covered by current layer
p.erase(std::remove_if(p.begin(), p.end(), [&current_poly](const ExPolygon& ex) {
return diff_ex(ex, current_poly).empty();
}), p.end());
// And now union it with current region
ExPolygons layer_polygons = to_expolygons(layer->m_regions[region_id]->slices.surfaces);
layer->m_regions[region_id]->slices.set(union_ex(layer_polygons, p), stInternal);
// Then remove it from all other regions, to avoid overlapping regions
for (size_t other_region = 0; other_region < this->num_printing_regions(); ++other_region) {
if (other_region == region_id) {
continue;
}
ExPolygons s = to_expolygons(layer->m_regions[other_region]->slices.surfaces);
layer->m_regions[other_region]->slices.set(diff_ex(s, p, ApplySafetyOffset::Yes), stInternal);
}
}
//layer->export_region_slices_to_svg_debug("layer_after_conical_overhang");
}

View file

@ -1910,14 +1910,15 @@ static inline void improve_ordering_by_two_exchanges_with_segment_flipping(Polyl
for (const FlipEdge &edge : edges) {
Polyline &pl = polylines[edge.source_index];
out.emplace_back(std::move(pl));
if (edge.p2 == pl.first_point().cast<double>()) {
if (edge.p2 == out.back().first_point().cast<double>()) {
// Polyline is flipped.
out.back().reverse();
} else {
// Polyline is not flipped.
assert(edge.p1 == pl.first_point().cast<double>());
assert(edge.p1 == out.back().first_point().cast<double>());
}
}
polylines = out;
#ifndef NDEBUG
double cost_final = cost();

View file

@ -319,6 +319,13 @@ std::vector<double> layer_height_profile_adaptive(const SlicingParameters& slici
else if (layer_height_profile.back() > height && layer_height_profile.back() - height > LAYER_HEIGHT_CHANGE_STEP)
height = layer_height_profile.back() - LAYER_HEIGHT_CHANGE_STEP;
for (auto const& [range,options] : object.layer_config_ranges) {
if ( print_z >= range.first && print_z <= range.second) {
height = options.opt_float("layer_height");
break;
};
};
layer_height_profile.push_back(print_z);
layer_height_profile.push_back(height);
print_z += height;
@ -444,6 +451,7 @@ std::vector<double> smooth_height_profile(const std::vector<double>& profile, co
}
void adjust_layer_height_profile(
const ModelObject &model_object,
const SlicingParameters &slicing_params,
std::vector<coordf_t> &layer_height_profile,
coordf_t z,
@ -478,6 +486,11 @@ void adjust_layer_height_profile(
}
}
for (auto const& [range,options] : model_object.layer_config_ranges) {
if (z >= range.first - current_layer_height && z <= range.second + current_layer_height)
return;
};
// 2) Is it possible to apply the delta?
switch (action) {
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:

View file

@ -176,6 +176,7 @@ enum LayerHeightEditActionType : unsigned int {
};
void adjust_layer_height_profile(
const ModelObject &model_object,
const SlicingParameters &slicing_params,
std::vector<coordf_t> &layer_height_profile,
coordf_t z,

View file

@ -1665,7 +1665,7 @@ TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const {
} else {
// In case this is leaf, we better save information about its state.
int n = int(tr.get_state());
if (n < static_cast<size_t>(EnforcerBlockerType::ExtruderMax))
if (n <= static_cast<size_t>(EnforcerBlockerType::ExtruderMax))
data.used_states[n] = true;
if (n >= 3) {

View file

@ -256,7 +256,7 @@ public:
// Bit stream containing splitting information.
std::vector<bool> bitstream;
// Array indicating which triangle state types are used (encoded inside bitstream).
std::vector<bool> used_states { std::vector<bool>(static_cast<size_t>(EnforcerBlockerType::ExtruderMax), false) };
std::vector<bool> used_states { std::vector<bool>(static_cast<size_t>(EnforcerBlockerType::ExtruderMax) + 1, false) };
TriangleSplittingData() = default;
@ -270,7 +270,7 @@ public:
// Reset all used states before they are recomputed based on the bitstream.
void reset_used_states() {
used_states.resize(static_cast<size_t>(EnforcerBlockerType::ExtruderMax), false);
used_states.resize(static_cast<size_t>(EnforcerBlockerType::ExtruderMax) + 1, false);
std::fill(used_states.begin(), used_states.end(), false);
}

View file

@ -46,13 +46,20 @@ double CalibPressureAdvance::e_per_mm(
return line_flow.mm3_per_mm() * print_flow_ratio / filament_area ;
}
std::string CalibPressureAdvance::convert_number_to_string(double num) const
std::string CalibPressureAdvance::convert_number_to_string(double num, unsigned int precision) const
{
auto sNumber = std::to_string(num);
sNumber.erase(sNumber.find_last_not_of('0') + 1, std::string::npos);
sNumber.erase(sNumber.find_last_not_of('.') + 1, std::string::npos);
std::ostringstream stream;
return sNumber;
if (precision) {
/* if number is > 1000 then there are no way we'll fit fractional part into 5 glyphs, so
* in this case we keep full precision.
* Otherwise we reduce precision by 1 to accomodate decimal separator */
stream << std::setprecision(num >= 1000 ? precision : precision - 1);
}
stream << num;
return stream.str();
}
std::string CalibPressureAdvance::draw_digit(
@ -201,12 +208,12 @@ std::string CalibPressureAdvance::draw_number(double
double speed,
GCodeWriter &writer)
{
auto sNumber = convert_number_to_string(value);
auto sNumber = convert_number_to_string(value, m_number_len);
std::stringstream gcode;
gcode << writer.set_speed(speed);
for (std::string::size_type i = 0; i < sNumber.length(); ++i) {
if (i > m_max_number_len) {
if (i >= m_number_len) {
break;
}
switch (mode) {
@ -537,6 +544,18 @@ CalibPressureAdvancePattern::CalibPressureAdvancePattern(
this->m_draw_digit_mode = DrawDigitMode::Bottom_To_Top;
refresh_setup(config, is_bbl_machine, model, origin);
}
double CalibPressureAdvancePattern::flow_val() const
{
double flow_mult = m_config.option<ConfigOptionFloats>("filament_flow_ratio")->get_at(0);
double nozzle_diameter = m_config.option<ConfigOptionFloats>("nozzle_diameter")->get_at(0);
double line_width = m_config.get_abs_value("line_width", nozzle_diameter);
double layer_height = m_config.get_abs_value("layer_height");
double speed = m_config.opt_float("outer_wall_speed");
Flow pattern_line = Flow(line_width, layer_height, m_config.option<ConfigOptionFloats>("nozzle_diameter")->get_at(0));
return speed * pattern_line.mm3_per_mm() * flow_mult;
};
void CalibPressureAdvancePattern::generate_custom_gcodes(const DynamicPrintConfig &config,
@ -564,7 +583,7 @@ void CalibPressureAdvancePattern::generate_custom_gcodes(const DynamicPrintConfi
draw_box_opt_args.is_filled = true;
draw_box_opt_args.num_perimeters = wall_count();
gcode << draw_box(m_writer, m_starting_point.x(), m_starting_point.y() + frame_size_y() + line_spacing_first_layer(),
glyph_tab_max_x() - m_starting_point.x(),
print_size_x(),
max_numbering_height() + line_spacing_first_layer() + m_glyph_padding_vertical * 2, draw_box_opt_args);
std::vector<CustomGCode::Item> gcode_items;
@ -593,6 +612,8 @@ void CalibPressureAdvancePattern::generate_custom_gcodes(const DynamicPrintConfi
// line numbering
if (i == 1) {
m_number_len = max_numbering_length();
gcode << m_writer.set_pressure_advance(m_params.start);
double number_e_per_mm = e_per_mm(line_width(), height_layer(),
@ -606,6 +627,18 @@ void CalibPressureAdvancePattern::generate_custom_gcodes(const DynamicPrintConfi
m_params.start + (j * m_params.step), m_draw_digit_mode, line_width(), number_e_per_mm,
speed_first_layer(), m_writer);
}
// flow value
int line_num = num_patterns + 2;
gcode << draw_number(glyph_start_x(line_num), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
flow_val(), m_draw_digit_mode, line_width(), number_e_per_mm,
speed_first_layer(), m_writer);
// acceleration
line_num = num_patterns + 4;
gcode << draw_number(glyph_start_x(line_num), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
m_config.opt_float("default_acceleration"), m_draw_digit_mode, line_width(), number_e_per_mm,
speed_first_layer(), m_writer);
}
@ -790,7 +823,7 @@ double CalibPressureAdvancePattern::glyph_tab_max_x() const
(glyph_length_x() - line_width() / 2) + padding;
}
double CalibPressureAdvancePattern::max_numbering_height() const
size_t CalibPressureAdvancePattern::max_numbering_length() const
{
std::string::size_type most_characters = 0;
const int num_patterns = get_num_patterns();
@ -804,9 +837,18 @@ double CalibPressureAdvancePattern::max_numbering_height() const
}
}
most_characters = std::min(most_characters, m_max_number_len);
std::string sAccel = convert_number_to_string(m_config.opt_float("default_acceleration"));
most_characters = std::max(most_characters, sAccel.length());
return (most_characters * m_digit_segment_len) + ((most_characters - 1) * m_digit_gap_len);
/* don't actually check flow value: we'll print as many fractional digits as fits */
return std::min(most_characters, m_max_number_len);
}
double CalibPressureAdvancePattern::max_numbering_height() const
{
std::string::size_type num_characters = max_numbering_length();
return (num_characters * m_digit_segment_len) + ((num_characters - 1) * m_digit_gap_len);
}
double CalibPressureAdvancePattern::pattern_shift() const

View file

@ -155,7 +155,7 @@ protected:
double e_per_mm(double line_width, double layer_height, float nozzle_diameter, float filament_diameter, float print_flow_ratio) const;
double speed_adjust(int speed) const { return speed * 60; };
std::string convert_number_to_string(double num) const;
std::string convert_number_to_string(double num, unsigned precision = 0) const;
double number_spacing() const { return m_digit_segment_len + m_digit_gap_len; };
std::string draw_digit(double startx,
double starty,
@ -188,6 +188,7 @@ protected:
const double m_digit_segment_len{2};
const double m_digit_gap_len{1};
const std::string::size_type m_max_number_len{5};
std::string::size_type m_number_len{m_max_number_len}; /* Current length of number labels */
};
class CalibPressureAdvanceLine : public CalibPressureAdvance
@ -255,6 +256,7 @@ public:
double print_size_x() const { return object_size_x() + pattern_shift(); };
double print_size_y() const { return object_size_y(); };
double max_layer_z() const { return height_first_layer() + ((m_num_layers - 1) * height_layer()); };
double flow_val() const;
void generate_custom_gcodes(const DynamicPrintConfig &config, bool is_bbl_machine, Model &model, const Vec3d &origin);
@ -264,8 +266,18 @@ public:
protected:
double speed_first_layer() const { return m_config.option<ConfigOptionFloat>("initial_layer_speed")->value; };
double speed_perimeter() const { return m_config.option<ConfigOptionFloat>("outer_wall_speed")->value; };
double line_width_first_layer() const { return m_config.get_abs_value("initial_layer_line_width"); };
double line_width() const { return m_config.get_abs_value("line_width"); };
double line_width_first_layer() const
{
// TODO: FIXME: find out current filament/extruder?
const double nozzle_diameter = m_config.opt_float("nozzle_diameter", 0);
return m_config.get_abs_value("initial_layer_line_width", nozzle_diameter);
};
double line_width() const
{
// TODO: FIXME: find out current filament/extruder?
const double nozzle_diameter = m_config.opt_float("nozzle_diameter", 0);
return m_config.get_abs_value("line_width", nozzle_diameter);
};
int wall_count() const { return m_config.option<ConfigOptionInt>("wall_loops")->value; };
private:
@ -296,6 +308,7 @@ private:
double glyph_length_x() const;
double glyph_tab_max_x() const;
double max_numbering_height() const;
size_t max_numbering_length() const;
double pattern_shift() const;