mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-23 22:54:08 -06:00
Merge branch 'main' into bugfox/layer-cooling
This commit is contained in:
commit
f3b2dc26f5
778 changed files with 18275 additions and 4773 deletions
|
@ -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
|
||||
|
|
|
@ -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_*/
|
|
@ -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...
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 }; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(), [¤t_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");
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue