mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-19 05:31:04 +00:00
Merge branch 'main' into bugfox/layer-cooling
This commit is contained in:
778 files changed
+18275
-4773
No files matched your search
+23
-7
@@ -119,6 +119,10 @@ inline cInt Round(double val)
|
||||
return static_cast<cInt>((val < 0) ? (val - 0.5) : (val + 0.5));
|
||||
}
|
||||
|
||||
// Overriding the Eigen operators because we don't want to compare Z coordinate if IntPoint is 3 dimensional.
|
||||
inline bool operator==(const IntPoint &l, const IntPoint &r) { return l.x() == r.x() && l.y() == r.y(); }
|
||||
inline bool operator!=(const IntPoint &l, const IntPoint &r) { return l.x() != r.x() || l.y() != r.y(); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PolyTree methods ...
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -189,19 +193,25 @@ double Area(const Path &poly)
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
double Area(const OutRec &outRec)
|
||||
double Area(const OutPt *op)
|
||||
{
|
||||
OutPt *op = outRec.Pts;
|
||||
const OutPt *startOp = op;
|
||||
if (!op) return 0;
|
||||
double a = 0;
|
||||
do {
|
||||
a += (double)(op->Prev->Pt.x() + op->Pt.x()) * (double)(op->Prev->Pt.y() - op->Pt.y());
|
||||
op = op->Next;
|
||||
} while (op != outRec.Pts);
|
||||
} while (op != startOp);
|
||||
return a * 0.5;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
double Area(const OutRec &outRec)
|
||||
{
|
||||
return Area(outRec.Pts);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool PointIsVertex(const IntPoint &Pt, OutPt *pp)
|
||||
{
|
||||
OutPt *pp2 = pp;
|
||||
@@ -536,27 +546,32 @@ bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2)
|
||||
p = btmPt2->Next;
|
||||
while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next;
|
||||
double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt));
|
||||
return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
|
||||
|
||||
if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) &&
|
||||
std::min(dx1p, dx1n) == std::min(dx2p, dx2n))
|
||||
return Area(btmPt1) > 0; //if otherwise identical use orientation
|
||||
else
|
||||
return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Called by GetLowermostRec()
|
||||
OutPt* GetBottomPt(OutPt *pp)
|
||||
{
|
||||
OutPt* dups = 0;
|
||||
OutPt* dups = nullptr;
|
||||
OutPt* p = pp->Next;
|
||||
while (p != pp)
|
||||
{
|
||||
if (p->Pt.y() > pp->Pt.y())
|
||||
{
|
||||
pp = p;
|
||||
dups = 0;
|
||||
dups = nullptr;
|
||||
}
|
||||
else if (p->Pt.y() == pp->Pt.y() && p->Pt.x() <= pp->Pt.x())
|
||||
{
|
||||
if (p->Pt.x() < pp->Pt.x())
|
||||
{
|
||||
dups = 0;
|
||||
dups = nullptr;
|
||||
pp = p;
|
||||
} else
|
||||
{
|
||||
@@ -577,6 +592,7 @@ OutPt* GetBottomPt(OutPt *pp)
|
||||
}
|
||||
return pp;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool Pt2IsBetweenPt1AndPt3(const IntPoint &pt1,
|
||||
|
||||
@@ -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 }; }
|
||||
|
||||
+254
-93
@@ -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;
|
||||
|
||||
+44
-34
@@ -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
|
||||
|
||||
+74
-47
@@ -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;
|
||||
}
|
||||
|
||||
+286
-107
@@ -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
|
||||
|
||||
+57
-12
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+53
-11
@@ -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
|
||||
|
||||
+16
-3
@@ -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;
|
||||
|
||||
|
||||
@@ -145,6 +145,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
#GUI/Gizmos/GLGizmoFaceDetector.hpp
|
||||
GUI/Gizmos/GLGizmoMeasure.cpp
|
||||
GUI/Gizmos/GLGizmoMeasure.hpp
|
||||
GUI/Gizmos/GLGizmoAssembly.cpp
|
||||
GUI/Gizmos/GLGizmoAssembly.hpp
|
||||
GUI/Gizmos/GLGizmoSeam.cpp
|
||||
GUI/Gizmos/GLGizmoSeam.hpp
|
||||
#GUI/Gizmos/GLGizmoText.cpp
|
||||
|
||||
@@ -84,7 +84,11 @@ std::vector<Slic3r::ColorRGBA> get_extruders_colors()
|
||||
|
||||
float FullyTransparentMaterialThreshold = 0.1f;
|
||||
float FullTransparentModdifiedToFixAlpha = 0.3f;
|
||||
float FULL_BLACK_THRESHOLD = 0.18f;
|
||||
// Be careful changing this value because it could break thumbnail color due to rounding error!
|
||||
// The color rendering on BambuLab's "send to printer" screen relies on the assumption that this color can be accurately rendered by OpenGL,
|
||||
// value like 0.18f could not because in C++ (int)(0.18f * 255) == 45 however in OpenGL it renders this as 46
|
||||
// which breaks the `SelectMachineDialog::record_edge_pixels_data()` function!
|
||||
float FULL_BLACK_THRESHOLD = 0.2f;
|
||||
|
||||
Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::ColorRGBA &colors)
|
||||
{
|
||||
@@ -671,12 +675,13 @@ std::vector<int> GLVolumeCollection::load_object(
|
||||
int obj_idx,
|
||||
const std::vector<int> &instance_idxs,
|
||||
const std::string &color_by,
|
||||
bool opengl_initialized)
|
||||
bool opengl_initialized,
|
||||
bool need_raycaster)
|
||||
{
|
||||
std::vector<int> volumes_idx;
|
||||
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx)
|
||||
for (int instance_idx : instance_idxs)
|
||||
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, opengl_initialized));
|
||||
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, opengl_initialized, false, false, need_raycaster));
|
||||
return volumes_idx;
|
||||
}
|
||||
|
||||
@@ -689,7 +694,8 @@ int GLVolumeCollection::load_object_volume(
|
||||
const std::string &color_by,
|
||||
bool opengl_initialized,
|
||||
bool in_assemble_view,
|
||||
bool use_loaded_id)
|
||||
bool use_loaded_id,
|
||||
bool need_raycaster)
|
||||
{
|
||||
const ModelVolume *model_volume = model_object->volumes[volume_idx];
|
||||
const int extruder_id = model_volume->extruder_id();
|
||||
@@ -707,7 +713,7 @@ int GLVolumeCollection::load_object_volume(
|
||||
v.model.init_from(mesh, true);
|
||||
#else
|
||||
v.model.init_from(*mesh);
|
||||
v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh);
|
||||
if (need_raycaster) { v.mesh_raycaster = std::make_unique<GUI::MeshRaycaster>(mesh); }
|
||||
#endif // ENABLE_SMOOTH_NORMALS
|
||||
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
|
||||
|
||||
|
||||
@@ -439,7 +439,8 @@ public:
|
||||
int obj_idx,
|
||||
const std::vector<int> &instance_idxs,
|
||||
const std::string &color_by,
|
||||
bool opengl_initialized);
|
||||
bool opengl_initialized,
|
||||
bool need_raycaster = true);
|
||||
|
||||
int load_object_volume(
|
||||
const ModelObject *model_object,
|
||||
@@ -449,7 +450,8 @@ public:
|
||||
const std::string &color_by,
|
||||
bool opengl_initialized,
|
||||
bool in_assemble_view = false,
|
||||
bool use_loaded_id = false);
|
||||
bool use_loaded_id = false,
|
||||
bool need_raycaster = true);
|
||||
// Load SLA auxiliary GLVolumes (for support trees or pad).
|
||||
void load_object_auxiliary(
|
||||
const SLAPrintObject *print_object,
|
||||
|
||||
@@ -50,14 +50,14 @@ static const std::vector<std::string> filament_vendors =
|
||||
"Fil X", "GEEETECH", "Giantarm", "Gizmo Dorks", "GreenGate3D",
|
||||
"HATCHBOX", "Hello3D", "IC3D", "IEMAI", "IIID Max",
|
||||
"INLAND", "iProspect", "iSANMATE", "Justmaker", "Keene Village Plastics",
|
||||
"Kexcelled", "MakerBot", "MatterHackers", "MIKA3D", "NinjaTek",
|
||||
"Nobufil", "Novamaker", "OVERTURE", "OVVNYXE", "Polymaker",
|
||||
"Priline", "Printed Solid", "Protopasta", "Prusament", "Push Plastic",
|
||||
"R3D", "Re-pet3D", "Recreus", "Regen", "RatRig",
|
||||
"Sain SMART", "SliceWorx", "Snapmaker", "SnoLabs", "Spectrum",
|
||||
"SUNLU", "TTYT3D", "Tianse", "UltiMaker", "Valment",
|
||||
"Verbatim", "VO3D", "Voxelab", "VOXELPLA", "YOOPAI",
|
||||
"Yousu", "Ziro", "Zyltech"};
|
||||
"Kexcelled", "LDO", "MakerBot", "MatterHackers", "MIKA3D",
|
||||
"NinjaTek", "Nobufil", "Novamaker", "OVERTURE", "OVVNYXE",
|
||||
"Polymaker", "Priline", "Printed Solid", "Protopasta", "Prusament",
|
||||
"Push Plastic", "R3D", "Re-pet3D", "Recreus", "Regen",
|
||||
"RatRig", "Sain SMART", "SliceWorx", "Snapmaker", "SnoLabs",
|
||||
"Spectrum", "SUNLU", "TTYT3D", "Tianse", "UltiMaker",
|
||||
"Valment", "Verbatim", "VO3D", "Voxelab", "VOXELPLA",
|
||||
"YOOPAI", "Yousu", "Ziro", "Zyltech"};
|
||||
|
||||
static const std::vector<std::string> filament_types = {"PLA", "rPLA", "PLA+", "PLA Tough", "PETG", "ABS", "ASA", "FLEX", "HIPS", "PA", "PACF",
|
||||
"NYLON", "PVA", "PVB", "PC", "PCABS", "PCTG", "PCCF", "PHA", "PP", "PEI", "PET",
|
||||
@@ -543,16 +543,26 @@ static char* read_json_file(const std::string &preset_path)
|
||||
|
||||
static std::string get_printer_nozzle_diameter(std::string printer_name) {
|
||||
|
||||
size_t index = printer_name.find(" nozzle");
|
||||
size_t index = printer_name.find(" nozzle)");
|
||||
if (std::string::npos == index) {
|
||||
return "";
|
||||
size_t index = printer_name.find(" nozzle");
|
||||
if (std::string::npos == index) {
|
||||
return "";
|
||||
}
|
||||
std::string nozzle = printer_name.substr(0, index);
|
||||
size_t last_space_index = nozzle.find_last_of(" ");
|
||||
if (std::string::npos == index) {
|
||||
return "";
|
||||
}
|
||||
return nozzle.substr(last_space_index + 1);
|
||||
} else {
|
||||
std::string nozzle = printer_name.substr(0, index);
|
||||
size_t last_bracket_index = nozzle.find_last_of("(");
|
||||
if (std::string::npos == index) {
|
||||
return "";
|
||||
}
|
||||
return nozzle.substr(last_bracket_index + 1);
|
||||
}
|
||||
std::string nozzle = printer_name.substr(0, index);
|
||||
size_t last_space_index = nozzle.find_last_of(" ");
|
||||
if (std::string::npos == index) {
|
||||
return "";
|
||||
}
|
||||
return nozzle.substr(last_space_index + 1);
|
||||
}
|
||||
|
||||
static void adjust_dialog_in_screen(DPIDialog* dialog) {
|
||||
@@ -1179,10 +1189,11 @@ wxArrayString CreateFilamentPresetDialog::get_filament_preset_choices()
|
||||
std::string preset_name = filament_preset->name;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " filament_id: " << filament_preset->filament_id << " preset name: " << filament_preset->name;
|
||||
size_t index_at = preset_name.find(" @");
|
||||
std::string cur_preset_name = preset_name;
|
||||
if (std::string::npos != index_at) {
|
||||
std::string cur_preset_name = preset_name.substr(0, index_at);
|
||||
preset_name_set.insert(from_u8(cur_preset_name));
|
||||
cur_preset_name = preset_name.substr(0, index_at);
|
||||
}
|
||||
preset_name_set.insert(from_u8(cur_preset_name));
|
||||
}
|
||||
assert(1 == preset_name_set.size());
|
||||
if (preset_name_set.size() > 1) {
|
||||
|
||||
@@ -159,7 +159,7 @@ void Downloader::start_download(const std::string& full_url)
|
||||
size_t id = get_next_id();
|
||||
std::string escaped_url = FileGet::escape_url(full_url.substr(results.length()));
|
||||
if (is_bambustudio_open(full_url) || (is_orca_open(full_url) && is_makerworld_link(full_url)))
|
||||
plater->request_model_download(escaped_url);
|
||||
plater->request_model_download(wxString::FromUTF8(escaped_url));
|
||||
else {
|
||||
std::string text(escaped_url);
|
||||
m_downloads.emplace_back(std::make_unique<Download>(id, std::move(escaped_url), this, m_dest_folder));
|
||||
@@ -254,4 +254,4 @@ void Downloader::set_download_state(int id, DownloadState state)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3123,7 +3123,7 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p
|
||||
instance_ids.resize(instance_index);
|
||||
|
||||
size_t current_volumes_count = m_shells.volumes.volumes.size();
|
||||
m_shells.volumes.load_object(model_obj, object_idx, instance_ids, "object", initialized);
|
||||
m_shells.volumes.load_object(model_obj, object_idx, instance_ids, "object", initialized, false);
|
||||
|
||||
// adjust shells' z if raft is present
|
||||
const SlicingParameters& slicing_parameters = obj->slicing_parameters();
|
||||
|
||||
@@ -590,7 +590,7 @@ void GLCanvas3D::LayersEditing::adjust_layer_height_profile()
|
||||
{
|
||||
this->update_slicing_parameters();
|
||||
PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile);
|
||||
Slic3r::adjust_layer_height_profile(*m_slicing_parameters, m_layer_height_profile, this->last_z, this->strength, this->band_width, this->last_action);
|
||||
Slic3r::adjust_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile, this->last_z, this->strength, this->band_width, this->last_action);
|
||||
m_layers_texture.valid = false;
|
||||
}
|
||||
|
||||
@@ -4445,7 +4445,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||
logical_pos = logical_pos.cwiseQuotient(Vec2d(factor, factor));
|
||||
#endif // ENABLE_RETINA_GL
|
||||
|
||||
if (!m_mouse.ignore_right_up) {
|
||||
if (!m_mouse.ignore_right_up && m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined) {
|
||||
//BBS post right click event
|
||||
if (!m_hover_plate_idxs.empty()) {
|
||||
post_event(RBtnPlateEvent(EVT_GLCANVAS_PLATE_RIGHT_CLICK, { logical_pos, m_hover_plate_idxs.front() }));
|
||||
@@ -5939,7 +5939,7 @@ void GLCanvas3D::render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
||||
|
||||
ColorRGBA new_color = adjust_color_for_rendering(curr_color);
|
||||
if (ban_light) {
|
||||
new_color[3] = (255 - vol->extruder_id) / 255.0f;
|
||||
new_color[3] =(255 - (vol->extruder_id -1))/255.0f;
|
||||
}
|
||||
vol->model.set_color(new_color);
|
||||
shader->set_uniform("volume_world_matrix", vol->world_matrix());
|
||||
|
||||
@@ -743,6 +743,7 @@ public:
|
||||
m_scene_raycaster.set_gizmos_on_top(value);
|
||||
}
|
||||
|
||||
float get_explosion_ratio() { return m_explosion_ratio; }
|
||||
void reset_explosion_ratio() { m_explosion_ratio = 1.0; }
|
||||
void on_change_color_mode(bool is_dark, bool reinit = true);
|
||||
const bool get_dark_mode_status() { return m_is_dark; }
|
||||
|
||||
@@ -1412,5 +1412,84 @@ GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int sec
|
||||
return data;
|
||||
}
|
||||
|
||||
GLModel::Geometry init_plane_data(const indexed_triangle_set& its, const std::vector<int>& triangle_indices, float normal_offset)
|
||||
{
|
||||
GLModel::Geometry init_data;
|
||||
init_data.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
||||
init_data.reserve_indices(3 * triangle_indices.size());
|
||||
init_data.reserve_vertices(3 * triangle_indices.size());
|
||||
unsigned int i = 0;
|
||||
for (int idx : triangle_indices) {
|
||||
Vec3f v0 = its.vertices[its.indices[idx][0]];
|
||||
Vec3f v1 = its.vertices[its.indices[idx][1]];
|
||||
Vec3f v2 = its.vertices[its.indices[idx][2]];
|
||||
const Vec3f n = (v1 - v0).cross(v2 - v0).normalized();
|
||||
if (std::abs(normal_offset) > 0.0) {
|
||||
v0 = v0 + n * normal_offset;
|
||||
v1 = v1 + n * normal_offset;
|
||||
v2 = v2 + n * normal_offset;
|
||||
}
|
||||
init_data.add_vertex(v0, n);
|
||||
init_data.add_vertex(v1, n);
|
||||
init_data.add_vertex(v2, n);
|
||||
init_data.add_triangle(i, i + 1, i + 2);
|
||||
i += 3;
|
||||
}
|
||||
|
||||
return init_data;
|
||||
}
|
||||
|
||||
GLModel::Geometry init_torus_data(unsigned int primary_resolution,
|
||||
unsigned int secondary_resolution,
|
||||
const Vec3f & center,
|
||||
float radius,
|
||||
float thickness,
|
||||
const Vec3f & model_axis,
|
||||
const Transform3f &world_trafo)
|
||||
{
|
||||
const unsigned int torus_sector_count = std::max<unsigned int>(4, primary_resolution);
|
||||
const unsigned int section_sector_count = std::max<unsigned int>(4, secondary_resolution);
|
||||
const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count);
|
||||
const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count);
|
||||
|
||||
GLModel::Geometry data;
|
||||
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
||||
data.reserve_vertices(torus_sector_count * section_sector_count);
|
||||
data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3);
|
||||
|
||||
// vertices
|
||||
const Transform3f local_to_world_matrix = world_trafo * Geometry::translation_transform(center.cast<double>()).cast<float>() *
|
||||
Eigen::Quaternion<float>::FromTwoVectors(Vec3f::UnitZ(), model_axis);
|
||||
for (unsigned int i = 0; i < torus_sector_count; ++i) {
|
||||
const float section_angle = torus_sector_step * i;
|
||||
const Vec3f radius_dir(std::cos(section_angle), std::sin(section_angle), 0.0f);
|
||||
const Vec3f local_section_center = radius * radius_dir;
|
||||
const Vec3f world_section_center = local_to_world_matrix * local_section_center;
|
||||
const Vec3f local_section_normal = local_section_center.normalized().cross(Vec3f::UnitZ()).normalized();
|
||||
const Vec3f world_section_normal = (Vec3f) (local_to_world_matrix.matrix().block(0, 0, 3, 3) * local_section_normal).normalized();
|
||||
const Vec3f base_v = thickness * radius_dir;
|
||||
for (unsigned int j = 0; j < section_sector_count; ++j) {
|
||||
const Vec3f v = Eigen::AngleAxisf(section_sector_step * j, world_section_normal) * base_v;
|
||||
data.add_vertex(world_section_center + v, (Vec3f) v.normalized());
|
||||
}
|
||||
}
|
||||
|
||||
// triangles
|
||||
for (unsigned int i = 0; i < torus_sector_count; ++i) {
|
||||
const unsigned int ii = i * section_sector_count;
|
||||
const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count;
|
||||
for (unsigned int j = 0; j < section_sector_count; ++j) {
|
||||
const unsigned int j_next = (j + 1) % section_sector_count;
|
||||
const unsigned int i0 = ii + j;
|
||||
const unsigned int i1 = ii_next + j;
|
||||
const unsigned int i2 = ii_next + j_next;
|
||||
const unsigned int i3 = ii + j_next;
|
||||
data.add_triangle(i0, i1, i2);
|
||||
data.add_triangle(i0, i2, i3);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
@@ -247,7 +247,15 @@ namespace GUI {
|
||||
// the origin of the torus is in its center
|
||||
GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness);
|
||||
|
||||
} // namespace GUI
|
||||
GLModel::Geometry init_plane_data(const indexed_triangle_set &its, const std::vector<int> &triangle_indices,float normal_offset = 0.0f);
|
||||
GLModel::Geometry init_torus_data(unsigned int primary_resolution,
|
||||
unsigned int secondary_resolution,
|
||||
const Vec3f & center,
|
||||
float radius,
|
||||
float thickness,
|
||||
const Vec3f & model_axis,
|
||||
const Transform3f &world_trafo);
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GLModel_hpp_
|
||||
|
||||
@@ -792,8 +792,8 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas
|
||||
}
|
||||
|
||||
if (m_type == Normal && item->get_state() != GLToolbarItem::Disabled) {
|
||||
// the item may get disabled during the action, if not, set it back to normal state
|
||||
item->set_state(GLToolbarItem::Normal);
|
||||
// the item may get disabled during the action, if not, set it back to hover state
|
||||
item->set_state(GLToolbarItem::Hover);
|
||||
parent.render();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4280,7 +4280,6 @@ void GUI_App::check_new_version_sf(bool show_tips, int by_user)
|
||||
best_pre = tag_version;
|
||||
best_pre_url = root.get<std::string>("html_url");
|
||||
best_pre_content = root.get<std::string>("body");
|
||||
best_pre.set_prerelease("Preview");
|
||||
}
|
||||
} else {
|
||||
if (best_release < tag_version) {
|
||||
@@ -4302,7 +4301,6 @@ void GUI_App::check_new_version_sf(bool show_tips, int by_user)
|
||||
best_pre = tag_version;
|
||||
best_pre_url = json_version.second.get<std::string>("html_url");
|
||||
best_pre_content = json_version.second.get<std::string>("body");
|
||||
best_pre.set_prerelease("Preview");
|
||||
}
|
||||
} else {
|
||||
if (best_release < tag_version) {
|
||||
@@ -5647,7 +5645,8 @@ bool GUI_App::check_and_keep_current_preset_changes(const wxString& caption, con
|
||||
bool is_called_from_configwizard = postponed_apply_of_keeped_changes != nullptr;
|
||||
|
||||
UnsavedChangesDialog dlg(caption, header, "", action_buttons);
|
||||
if (dlg.ShowModal() == wxID_CANCEL)
|
||||
bool no_need_change = dlg.getUpdateItemCount() == 0 ? true : false;
|
||||
if (!no_need_change && dlg.ShowModal() == wxID_CANCEL)
|
||||
return false;
|
||||
|
||||
auto reset_modifications = [this, is_called_from_configwizard]() {
|
||||
@@ -5662,7 +5661,7 @@ bool GUI_App::check_and_keep_current_preset_changes(const wxString& caption, con
|
||||
load_current_presets(false);
|
||||
};
|
||||
|
||||
if (dlg.discard())
|
||||
if (dlg.discard() || no_need_change)
|
||||
reset_modifications();
|
||||
else // save selected changes
|
||||
{
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
#include "GLGizmoAssembly.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/MeasureUtils.hpp"
|
||||
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
#include <numeric>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <future>
|
||||
#include <wx/clipbrd.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
GLGizmoAssembly::GLGizmoAssembly(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) :
|
||||
GLGizmoMeasure(parent, icon_filename, sprite_id)
|
||||
{
|
||||
m_measure_mode = EMeasureMode::ONLY_ASSEMBLY;
|
||||
}
|
||||
|
||||
std::string GLGizmoAssembly::on_get_name() const
|
||||
{
|
||||
if (!on_is_activable() && m_state == EState::Off) {
|
||||
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) {
|
||||
return _u8L("Assemble") + ":\n" + _u8L("Please confirm explosion ratio = 1 and select at least two volumes.");
|
||||
}
|
||||
else {
|
||||
return _u8L("Assemble") + ":\n" + _u8L("Please select at least two volumes.");
|
||||
}
|
||||
} else {
|
||||
return _u8L("Assemble");
|
||||
}
|
||||
}
|
||||
|
||||
bool GLGizmoAssembly::on_is_activable() const
|
||||
{
|
||||
const Selection& selection = m_parent.get_selection();
|
||||
if (selection.is_wipe_tower()) {
|
||||
return false;
|
||||
}
|
||||
const int selection_volumes_count = 2;
|
||||
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasAssembleView) {
|
||||
if (abs(m_parent.get_explosion_ratio() - 1.0f) < 1e-2 && selection.volumes_count() >= selection_volumes_count) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return selection.volumes_count() >= selection_volumes_count;
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmoAssembly::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
static std::optional<Measure::SurfaceFeature> last_feature;
|
||||
static EMode last_mode = EMode::FeatureSelection;
|
||||
static SelectedFeatures last_selected_features;
|
||||
|
||||
static float last_y = 0.0f;
|
||||
static float last_h = 0.0f;
|
||||
|
||||
if (m_editing_distance)
|
||||
return;
|
||||
m_current_active_imgui_id = ImGui::GetActiveID();
|
||||
// adjust window position to avoid overlap the view toolbar
|
||||
const float win_h = ImGui::GetWindowHeight();
|
||||
y = std::min(y, bottom_limit - win_h);
|
||||
GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f);
|
||||
if (last_h != win_h || last_y != y) {
|
||||
// ask canvas for another frame to render the window in the correct position
|
||||
m_imgui->set_requires_extra_frame();
|
||||
if (last_h != win_h)
|
||||
last_h = win_h;
|
||||
if (last_y != y)
|
||||
last_y = y;
|
||||
}
|
||||
// Orca
|
||||
ImGuiWrapper::push_toolbar_style(m_parent.get_scale());
|
||||
GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
|
||||
init_render_input_window();
|
||||
|
||||
float moving_size = m_imgui->calc_text_size(_L("(Moving)")).x;
|
||||
float combox_content_size = m_imgui->calc_text_size(_L("Point and point assembly")).x*1.1 + ImGui::GetStyle().FramePadding.x * 18.0f;
|
||||
float caption_size = moving_size + 2 * m_space_size;
|
||||
if (render_assembly_mode_combo(caption_size + 0.5 * m_space_size, combox_content_size)) {
|
||||
;
|
||||
}
|
||||
show_selection_ui();
|
||||
show_face_face_assembly_common();
|
||||
ImGui::Separator();
|
||||
show_face_face_assembly_senior();
|
||||
show_distance_xyz_ui();
|
||||
render_input_window_warning(m_same_model_object);
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f));
|
||||
float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y;
|
||||
float caption_max = 0.f;
|
||||
float total_text_max = 0.f;
|
||||
for (const auto &t : std::array<std::string, 3>{"point_selection", "reset", "unselect"}) {
|
||||
caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x);
|
||||
total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x);
|
||||
}
|
||||
show_tooltip_information(caption_max, x, get_cur_y);
|
||||
|
||||
float f_scale =m_parent.get_gizmos_manager().get_layout_scale();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale));
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
if (last_feature != m_curr_feature || last_mode != m_mode || last_selected_features != m_selected_features) {
|
||||
// the dialog may have changed its size, ask for an extra frame to render it properly
|
||||
last_feature = m_curr_feature;
|
||||
last_mode = m_mode;
|
||||
last_selected_features = m_selected_features;
|
||||
m_imgui->set_requires_extra_frame();
|
||||
}
|
||||
m_last_active_item_imgui = m_current_active_imgui_id;
|
||||
GizmoImguiEnd();
|
||||
// Orca
|
||||
ImGuiWrapper::pop_toolbar_style();
|
||||
}
|
||||
|
||||
void GLGizmoAssembly::render_input_window_warning(bool same_model_object)
|
||||
{
|
||||
if (wxGetApp().plater()->canvas3D()->get_canvas_type() == GLCanvas3D::ECanvasType::CanvasView3D) {
|
||||
if (m_hit_different_volumes.size() == 2) {
|
||||
if (same_model_object == false) {
|
||||
m_imgui->warning_text(_L("Warning") + ": " +
|
||||
_L("It is recommended to assemble the objects first,\nbecause the objects is restriced to bed \nand only parts can be lifted."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GLGizmoAssembly::render_assembly_mode_combo(double label_width, float item_width)
|
||||
{
|
||||
ImGui::AlignTextToFramePadding();
|
||||
int selection_idx = int(m_assembly_mode);
|
||||
std::vector<std::string> modes = {_u8L("Face and face assembly"), _u8L("Point and point assembly")};
|
||||
bool is_changed = false;
|
||||
|
||||
ImGuiWrapper::push_combo_style(m_parent.get_scale());
|
||||
if (render_combo(_u8L("Mode"), modes, selection_idx, label_width, item_width)) {
|
||||
is_changed = true;
|
||||
switch_to_mode((AssemblyMode) selection_idx);
|
||||
}
|
||||
ImGuiWrapper::pop_combo_style();
|
||||
return is_changed;
|
||||
}
|
||||
|
||||
void GLGizmoAssembly::switch_to_mode(AssemblyMode new_mode)
|
||||
{
|
||||
m_assembly_mode = new_mode;
|
||||
reset_all_feature();
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef slic3r_GLGizmoAssembly_hpp_
|
||||
#define slic3r_GLGizmoAssembly_hpp_
|
||||
|
||||
#include "GLGizmoMeasure.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
class GLGizmoAssembly : public GLGizmoMeasure
|
||||
{
|
||||
|
||||
public:
|
||||
GLGizmoAssembly(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||
/// <summary>
|
||||
/// Apply rotation on select plane
|
||||
/// </summary>
|
||||
/// <param name="mouse_event">Keep information about mouse click</param>
|
||||
/// <returns>Return True when use the information otherwise False.</returns>
|
||||
//bool on_mouse(const wxMouseEvent &mouse_event) override;
|
||||
//void data_changed(bool is_serializing) override;
|
||||
//bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down) override;
|
||||
|
||||
bool wants_enter_leave_snapshots() const override { return true; }
|
||||
std::string get_gizmo_entering_text() const override { return _u8L("Entering Assembly gizmo"); }
|
||||
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Assembly gizmo"); }
|
||||
protected:
|
||||
//bool on_init() override;
|
||||
std::string on_get_name() const override;
|
||||
bool on_is_activable() const override;
|
||||
//void on_render() override;
|
||||
//void on_set_state() override;
|
||||
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||
|
||||
void render_input_window_warning(bool same_model_object) override;
|
||||
bool render_assembly_mode_combo(double label_width, float item_width);
|
||||
|
||||
void switch_to_mode(AssemblyMode new_mode);
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GLGizmoAssembly_hpp_
|
||||
@@ -194,6 +194,39 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color)
|
||||
}
|
||||
}
|
||||
|
||||
bool GLGizmoBase::render_combo(const std::string &label, const std::vector<std::string> &lines, int &selection_idx, float label_width, float item_width)
|
||||
{
|
||||
ImGuiWrapper::push_combo_style(m_parent.get_scale());
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(label);
|
||||
ImGui::SameLine(label_width);
|
||||
ImGui::PushItemWidth(item_width);
|
||||
|
||||
size_t selection_out = selection_idx;
|
||||
|
||||
const char *selected_str = (selection_idx >= 0 && selection_idx < int(lines.size())) ? lines[selection_idx].c_str() : "";
|
||||
if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) {
|
||||
for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
|
||||
ImGui::PushID(int(line_idx));
|
||||
if (ImGui::Selectable("", line_idx == selection_idx)) selection_out = line_idx;
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", lines[line_idx].c_str());
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
bool is_changed = selection_idx != selection_out;
|
||||
selection_idx = selection_out;
|
||||
|
||||
//if (is_changed) update_connector_shape();
|
||||
ImGuiWrapper::pop_combo_style();
|
||||
|
||||
return is_changed;
|
||||
}
|
||||
|
||||
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
|
||||
: m_parent(parent)
|
||||
, m_group_id(-1)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "slic3r/GUI/GLModel.hpp"
|
||||
#include "slic3r/GUI/MeshUtils.hpp"
|
||||
#include "slic3r/GUI/SceneRaycaster.hpp"
|
||||
#include "slic3r/GUI/3DScene.hpp"
|
||||
|
||||
#include <cereal/archives/binary.hpp>
|
||||
|
||||
@@ -148,6 +149,9 @@ protected:
|
||||
|
||||
bool m_is_dark_mode = false;
|
||||
|
||||
bool render_combo(const std::string &label, const std::vector<std::string> &lines,
|
||||
int &selection_idx, float label_width, float item_width);
|
||||
|
||||
public:
|
||||
GLGizmoBase(GLCanvas3D& parent,
|
||||
const std::string& icon_filename,
|
||||
@@ -181,6 +185,13 @@ public:
|
||||
|
||||
virtual bool apply_clipping_plane() { return true; }
|
||||
|
||||
/// <summary>
|
||||
/// Implement when want to process mouse events in gizmo
|
||||
/// Click, Right click, move, drag, ...
|
||||
/// </summary>
|
||||
/// <param name="mouse_event">Keep information about mouse click</param>
|
||||
/// <returns>Return True when use the information and don't want to propagate it otherwise False.</returns>
|
||||
virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; }
|
||||
unsigned int get_sprite_id() const { return m_sprite_id; }
|
||||
|
||||
int get_hover_id() const { return m_hover_id; }
|
||||
@@ -209,14 +220,6 @@ public:
|
||||
/// </summary>
|
||||
virtual void data_changed(bool is_serializing){};
|
||||
|
||||
/// <summary>
|
||||
/// Implement when want to process mouse events in gizmo
|
||||
/// Click, Right click, move, drag, ...
|
||||
/// </summary>
|
||||
/// <param name="mouse_event">Keep information about mouse click</param>
|
||||
/// <returns>Return True when use the information and don't want to propagate it otherwise False.</returns>
|
||||
virtual bool on_mouse(const wxMouseEvent &mouse_event) { return false; }
|
||||
|
||||
void register_raycasters_for_picking() { register_grabbers_for_picking(); on_register_raycasters_for_picking(); }
|
||||
void unregister_raycasters_for_picking() { unregister_grabbers_for_picking(); on_unregister_raycasters_for_picking(); }
|
||||
|
||||
|
||||
@@ -520,40 +520,6 @@ bool GLGizmoCut3D::render_cut_mode_combo()
|
||||
return is_changed;
|
||||
}
|
||||
|
||||
bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std::string>& lines, int& selection_idx)
|
||||
{
|
||||
ImGuiWrapper::push_combo_style(m_parent.get_scale());
|
||||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(label);
|
||||
ImGui::SameLine(m_label_width);
|
||||
ImGui::PushItemWidth(m_editing_window_width);
|
||||
|
||||
size_t selection_out = selection_idx;
|
||||
|
||||
const char* selected_str = (selection_idx >= 0 && selection_idx < int(lines.size())) ? lines[selection_idx].c_str() : "";
|
||||
if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) {
|
||||
for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
|
||||
ImGui::PushID(int(line_idx));
|
||||
if (ImGui::Selectable("", line_idx == selection_idx))
|
||||
selection_out = line_idx;
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", lines[line_idx].c_str());
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
bool is_changed = selection_idx != selection_out;
|
||||
selection_idx = selection_out;
|
||||
|
||||
//if (is_changed) update_connector_shape();
|
||||
ImGuiWrapper::pop_combo_style();
|
||||
|
||||
return is_changed;
|
||||
}
|
||||
|
||||
bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_in)
|
||||
{
|
||||
ImGui::AlignTextToFramePadding();
|
||||
@@ -2299,7 +2265,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, flo
|
||||
m_connector_style = int(CutConnectorStyle::Prism);
|
||||
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); });
|
||||
}
|
||||
if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style))
|
||||
if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style, m_label_width, m_editing_window_width))
|
||||
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); });
|
||||
m_imgui->disabled_end();
|
||||
|
||||
@@ -2308,7 +2274,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors, flo
|
||||
m_connector_shape_id = int(CutConnectorShape::Circle);
|
||||
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); });
|
||||
}
|
||||
if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id))
|
||||
if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id, m_label_width, m_editing_window_width))
|
||||
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); });
|
||||
m_imgui->disabled_end();
|
||||
|
||||
|
||||
@@ -335,7 +335,6 @@ private:
|
||||
void set_center(const Vec3d¢er, bool update_tbb = false);
|
||||
void switch_to_mode(size_t new_mode);
|
||||
bool render_cut_mode_combo();
|
||||
bool render_combo(const std::string&label, const std::vector<std::string>&lines, int&selection_idx);
|
||||
bool render_double_input(const std::string& label, double& value_in);
|
||||
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val = -0.1f, float max_tolerance = -0.1f);
|
||||
void render_move_center_input(int axis);
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -12,16 +12,85 @@
|
||||
namespace Slic3r {
|
||||
|
||||
enum class ModelVolumeType : int;
|
||||
|
||||
namespace Measure { class Measuring; }
|
||||
|
||||
|
||||
namespace GUI {
|
||||
|
||||
enum class SLAGizmoEventType : unsigned char;
|
||||
enum class EMeasureMode : unsigned char {
|
||||
ONLY_MEASURE,
|
||||
ONLY_ASSEMBLY
|
||||
};
|
||||
enum class AssemblyMode : unsigned char {
|
||||
FACE_FACE,
|
||||
POINT_POINT,
|
||||
};
|
||||
static const Slic3r::ColorRGBA SELECTED_1ST_COLOR = {0.25f, 0.75f, 0.75f, 1.0f};
|
||||
static const Slic3r::ColorRGBA SELECTED_2ND_COLOR = {0.75f, 0.25f, 0.75f, 1.0f};
|
||||
static const Slic3r::ColorRGBA NEUTRAL_COLOR = {0.5f, 0.5f, 0.5f, 1.0f};
|
||||
static const Slic3r::ColorRGBA HOVER_COLOR = ColorRGBA::GREEN();
|
||||
|
||||
static const int POINT_ID = 100;
|
||||
static const int EDGE_ID = 200;
|
||||
static const int CIRCLE_ID = 300;
|
||||
static const int PLANE_ID = 400;
|
||||
static const int SEL_SPHERE_1_ID = 501;
|
||||
static const int SEL_SPHERE_2_ID = 502;
|
||||
|
||||
static const float TRIANGLE_BASE = 10.0f;
|
||||
static const float TRIANGLE_HEIGHT = TRIANGLE_BASE * 1.618033f;
|
||||
|
||||
static const std::string CTRL_STR =
|
||||
#ifdef __APPLE__
|
||||
"⌘"
|
||||
#else
|
||||
"Ctrl"
|
||||
#endif //__APPLE__
|
||||
;
|
||||
|
||||
class TransformHelper
|
||||
{
|
||||
struct Cache
|
||||
{
|
||||
std::array<int, 4> viewport;
|
||||
Matrix4d ndc_to_ss_matrix;
|
||||
Transform3d ndc_to_ss_matrix_inverse;
|
||||
};
|
||||
static Cache s_cache;
|
||||
|
||||
public:
|
||||
static Vec3d model_to_world(const Vec3d &model, const Transform3d &world_matrix);
|
||||
static Vec4d world_to_clip(const Vec3d &world, const Matrix4d &projection_view_matrix);
|
||||
static Vec3d clip_to_ndc(const Vec4d &clip);
|
||||
static Vec2d ndc_to_ss(const Vec3d &ndc, const std::array<int, 4> &viewport);
|
||||
static Vec4d model_to_clip(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix);
|
||||
static Vec3d model_to_ndc(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix);
|
||||
static Vec2d model_to_ss(const Vec3d &model, const Transform3d &world_matrix, const Matrix4d &projection_view_matrix, const std::array<int, 4> &viewport);
|
||||
static Vec2d world_to_ss(const Vec3d &world, const Matrix4d &projection_view_matrix, const std::array<int, 4> &viewport);
|
||||
static const Matrix4d & ndc_to_ss_matrix(const std::array<int, 4> &viewport);
|
||||
static const Transform3d ndc_to_ss_matrix_inverse(const std::array<int, 4> &viewport);
|
||||
|
||||
private:
|
||||
static void update(const std::array<int, 4> &viewport);
|
||||
};
|
||||
|
||||
class GLGizmoMeasure : public GLGizmoBase
|
||||
{
|
||||
protected:
|
||||
using PickRaycaster = SceneRaycasterItem;
|
||||
enum GripperType {
|
||||
UNDEFINE,
|
||||
POINT,
|
||||
EDGE,
|
||||
CIRCLE,
|
||||
CIRCLE_1,
|
||||
CIRCLE_2,
|
||||
PLANE,
|
||||
PLANE_1,
|
||||
PLANE_2,
|
||||
SPHERE_1,
|
||||
SPHERE_2,
|
||||
};
|
||||
|
||||
enum class EMode : unsigned char
|
||||
{
|
||||
FeatureSelection,
|
||||
@@ -69,30 +138,50 @@ class GLGizmoMeasure : public GLGizmoBase
|
||||
}
|
||||
};
|
||||
|
||||
struct VolumeCacheItem
|
||||
{
|
||||
const ModelObject* object{ nullptr };
|
||||
const ModelInstance* instance{ nullptr };
|
||||
const ModelVolume* volume{ nullptr };
|
||||
Transform3d world_trafo;
|
||||
//struct VolumeCacheItem
|
||||
//{
|
||||
// const ModelObject* object{ nullptr };
|
||||
// const ModelInstance* instance{ nullptr };
|
||||
// const ModelVolume* volume{ nullptr };
|
||||
// Transform3d world_trafo;
|
||||
|
||||
bool operator == (const VolumeCacheItem& other) const {
|
||||
return this->object == other.object && this->instance == other.instance && this->volume == other.volume &&
|
||||
this->world_trafo.isApprox(other.world_trafo);
|
||||
}
|
||||
};
|
||||
// bool operator == (const VolumeCacheItem& other) const {
|
||||
// return this->object == other.object && this->instance == other.instance && this->volume == other.volume &&
|
||||
// this->world_trafo.isApprox(other.world_trafo);
|
||||
// }
|
||||
//};
|
||||
|
||||
std::vector<VolumeCacheItem> m_volumes_cache;
|
||||
//std::vector<VolumeCacheItem> m_volumes_cache;
|
||||
|
||||
EMode m_mode{ EMode::FeatureSelection };
|
||||
Measure::MeasurementResult m_measurement_result;
|
||||
Measure::AssemblyAction m_assembly_action;
|
||||
std::map<GLVolume*, std::shared_ptr<Measure::Measuring>> m_mesh_measure_map;
|
||||
std::shared_ptr<Measure::Measuring> m_curr_measuring{nullptr};
|
||||
|
||||
std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL
|
||||
|
||||
//first feature
|
||||
PickingModel m_sphere;
|
||||
PickingModel m_cylinder;
|
||||
PickingModel m_circle;
|
||||
PickingModel m_plane;
|
||||
struct CircleGLModel
|
||||
{
|
||||
PickingModel circle;
|
||||
Measure::SurfaceFeature *last_circle_feature{nullptr};
|
||||
float inv_zoom{0};
|
||||
};
|
||||
CircleGLModel m_curr_circle;
|
||||
CircleGLModel m_feature_circle_first;
|
||||
CircleGLModel m_feature_circle_second;
|
||||
void init_circle_glmodel(GripperType gripper_type, const Measure::SurfaceFeature &feature, CircleGLModel &circle_gl_model, float inv_zoom);
|
||||
|
||||
struct PlaneGLModel {
|
||||
int plane_idx{0};
|
||||
PickingModel plane;
|
||||
};
|
||||
PlaneGLModel m_curr_plane;
|
||||
PlaneGLModel m_feature_plane_first;
|
||||
PlaneGLModel m_feature_plane_second;
|
||||
void init_plane_glmodel(GripperType gripper_type, const Measure::SurfaceFeature &feature, PlaneGLModel &plane_gl_model);
|
||||
|
||||
struct Dimensioning
|
||||
{
|
||||
GLModel line;
|
||||
@@ -101,40 +190,38 @@ class GLGizmoMeasure : public GLGizmoBase
|
||||
};
|
||||
Dimensioning m_dimensioning;
|
||||
|
||||
// Uses a standalone raycaster and not the shared one because of the
|
||||
// difference in how the mesh is updated
|
||||
std::unique_ptr<MeshRaycaster> m_raycaster;
|
||||
|
||||
std::vector<GLModel> m_plane_models_cache;
|
||||
std::map<int, std::shared_ptr<SceneRaycasterItem>> m_raycasters;
|
||||
std::map<GLVolume*, std::shared_ptr<PickRaycaster>> m_mesh_raycaster_map;
|
||||
std::map<GripperType, std::shared_ptr<PickRaycaster>> m_gripper_id_raycast_map;
|
||||
std::vector<GLVolume*> m_hit_different_volumes;
|
||||
std::vector<GLVolume*> m_hit_order_volumes;
|
||||
GLVolume* m_last_hit_volume;
|
||||
//std::vector<std::shared_ptr<GLModel>> m_plane_models_cache;
|
||||
unsigned int m_last_active_item_imgui{0};
|
||||
Vec3d m_buffered_distance;
|
||||
Vec3d m_distance;
|
||||
double m_buffered_parallel_distance{0};
|
||||
double m_buffered_around_center{0};
|
||||
// used to keep the raycasters for point/center spheres
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_selected_sphere_raycasters;
|
||||
//std::vector<std::shared_ptr<PickRaycaster>> m_selected_sphere_raycasters;
|
||||
std::optional<Measure::SurfaceFeature> m_curr_feature;
|
||||
std::optional<Vec3d> m_curr_point_on_feature_position;
|
||||
struct SceneRaycasterState
|
||||
{
|
||||
std::shared_ptr<SceneRaycasterItem> raycaster{ nullptr };
|
||||
bool state{true};
|
||||
|
||||
};
|
||||
std::vector<SceneRaycasterState> m_scene_raycasters;
|
||||
|
||||
// These hold information to decide whether recalculation is necessary:
|
||||
float m_last_inv_zoom{ 0.0f };
|
||||
std::optional<Measure::SurfaceFeature> m_last_circle;
|
||||
std::optional<Measure::SurfaceFeature> m_last_circle_feature;
|
||||
int m_last_plane_idx{ -1 };
|
||||
|
||||
bool m_mouse_left_down{ false }; // for detection left_up of this gizmo
|
||||
|
||||
Vec2d m_mouse_pos{ Vec2d::Zero() };
|
||||
bool m_mouse_left_down_mesh_deal{false};//for pick mesh
|
||||
|
||||
KeyAutoRepeatFilter m_shift_kar_filter;
|
||||
|
||||
SelectedFeatures m_selected_features;
|
||||
bool m_pending_scale{ false };
|
||||
int m_pending_scale{ 0 };
|
||||
bool m_set_center_coincidence{false};
|
||||
bool m_editing_distance{ false };
|
||||
bool m_is_editing_distance_first_frame{ true };
|
||||
|
||||
bool m_can_set_xyz_distance{false};
|
||||
void update_if_needed();
|
||||
|
||||
void disable_scene_raycasters();
|
||||
@@ -148,7 +235,6 @@ class GLGizmoMeasure : public GLGizmoBase
|
||||
|
||||
public:
|
||||
GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||
|
||||
/// <summary>
|
||||
/// Apply rotation on select plane
|
||||
/// </summary>
|
||||
@@ -158,12 +244,12 @@ public:
|
||||
|
||||
void data_changed(bool is_serializing) override;
|
||||
|
||||
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||
virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||
|
||||
bool wants_enter_leave_snapshots() const override { return true; }
|
||||
std::string get_gizmo_entering_text() const override { return _u8L("Entering Measure gizmo"); }
|
||||
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Measure gizmo"); }
|
||||
std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); }
|
||||
//std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); }
|
||||
|
||||
protected:
|
||||
bool on_init() override;
|
||||
@@ -172,20 +258,66 @@ protected:
|
||||
void on_render() override;
|
||||
void on_set_state() override;
|
||||
|
||||
//virtual void on_render_for_picking() override;
|
||||
void show_selection_ui();
|
||||
void show_distance_xyz_ui();
|
||||
//void show_point_point_assembly();
|
||||
void show_face_face_assembly_common();
|
||||
void show_face_face_assembly_senior();
|
||||
void init_render_input_window();
|
||||
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||
virtual void on_register_raycasters_for_picking() override;
|
||||
virtual void on_unregister_raycasters_for_picking() override;
|
||||
|
||||
virtual void render_input_window_warning(bool same_model_object);
|
||||
void remove_selected_sphere_raycaster(int id);
|
||||
void update_measurement_result();
|
||||
|
||||
// Orca
|
||||
void show_tooltip_information(float caption_max, float x, float y);
|
||||
void reset_all_pick();
|
||||
void reset_gripper_pick(GripperType id,bool is_all = false);
|
||||
void register_single_mesh_pick();
|
||||
//void update_single_mesh_pick(GLVolume* v);
|
||||
|
||||
private:
|
||||
std::string format_double(double value);
|
||||
std::string format_vec3(const Vec3d &v);
|
||||
std::string surface_feature_type_as_string(Measure::SurfaceFeatureType type);
|
||||
std::string point_on_feature_type_as_string(Measure::SurfaceFeatureType type, int hover_id);
|
||||
std::string center_on_feature_type_as_string(Measure::SurfaceFeatureType type);
|
||||
bool is_feature_with_center(const Measure::SurfaceFeature &feature);
|
||||
Vec3d get_feature_offset(const Measure::SurfaceFeature &feature);
|
||||
|
||||
void reset_all_feature();
|
||||
void reset_feature1_render();
|
||||
void reset_feature2_render();
|
||||
void reset_feature1();
|
||||
void reset_feature2();
|
||||
bool is_two_volume_in_same_model_object();
|
||||
Measure::Measuring* get_measuring_of_mesh(GLVolume *v, Transform3d &tran);
|
||||
void update_world_plane_features(Measure::Measuring *cur_measuring, Measure::SurfaceFeature &feautre);
|
||||
void update_feature_by_tran(Measure::SurfaceFeature & feature);
|
||||
void set_distance(bool same_model_object, const Vec3d &displacement, bool take_shot = true);
|
||||
void set_to_parallel(bool same_model_object, bool take_shot = true, bool is_anti_parallel = false);
|
||||
void set_to_reverse_rotation(bool same_model_object,int feature_index);
|
||||
void set_to_around_center_of_faces(bool same_model_object,float rotate_degree);
|
||||
void set_to_center_coincidence(bool same_model_object);
|
||||
void set_parallel_distance(bool same_model_object,float dist);
|
||||
|
||||
bool is_pick_meet_assembly_mode(const SelectedFeatures::Item& item);
|
||||
protected:
|
||||
// This map holds all translated description texts, so they can be easily referenced during layout calculations
|
||||
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
|
||||
std::map<std::string, wxString> m_desc;
|
||||
bool m_show_reset_first_tip{false};
|
||||
bool m_selected_wrong_feature_waring_tip{false};
|
||||
EMeasureMode m_measure_mode{EMeasureMode::ONLY_MEASURE};
|
||||
AssemblyMode m_assembly_mode{AssemblyMode::FACE_FACE};
|
||||
bool m_flip_volume_2{false};
|
||||
float m_space_size;
|
||||
float m_input_size_max;
|
||||
bool m_use_inches;
|
||||
bool m_only_select_plane{false};
|
||||
std::string m_units;
|
||||
mutable bool m_same_model_object;
|
||||
mutable unsigned int m_current_active_imgui_id;
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoEmboss.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSVG.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoAssembly.hpp"
|
||||
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
@@ -61,7 +61,9 @@ std::vector<size_t> GLGizmosManager::get_selectable_idxs() const
|
||||
out.reserve(m_gizmos.size());
|
||||
if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
|
||||
for (size_t i = 0; i < m_gizmos.size(); ++i)
|
||||
if (m_gizmos[i]->get_sprite_id() == (unsigned int)MmuSegmentation)
|
||||
if (m_gizmos[i]->get_sprite_id() == (unsigned int) Measure ||
|
||||
m_gizmos[i]->get_sprite_id() == (unsigned int) Assembly ||
|
||||
m_gizmos[i]->get_sprite_id() == (unsigned int) MmuSegmentation)
|
||||
out.push_back(i);
|
||||
}
|
||||
else {
|
||||
@@ -158,9 +160,12 @@ void GLGizmosManager::switch_gizmos_icon_filename()
|
||||
case(EType::MeshBoolean):
|
||||
gizmo->set_icon_filename(m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg");
|
||||
break;
|
||||
case(EType::Measure):
|
||||
case (EType::Measure):
|
||||
gizmo->set_icon_filename(m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg");
|
||||
break;
|
||||
case (EType::Assembly):
|
||||
gizmo->set_icon_filename(m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -199,6 +204,7 @@ bool GLGizmosManager::init()
|
||||
m_gizmos.emplace_back(new GLGizmoEmboss(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Emboss));
|
||||
m_gizmos.emplace_back(new GLGizmoSVG(m_parent));
|
||||
m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure));
|
||||
m_gizmos.emplace_back(new GLGizmoAssembly(m_parent, m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg", EType::Assembly));
|
||||
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify));
|
||||
//m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++));
|
||||
//m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++));
|
||||
@@ -342,6 +348,8 @@ bool GLGizmosManager::check_gizmos_closed_except(EType type) const
|
||||
|
||||
void GLGizmosManager::set_hover_id(int id)
|
||||
{
|
||||
// Measure and assembly handles hover by itself
|
||||
if (m_current == EType::Measure || m_current == EType::Assembly) { return; }
|
||||
if (!m_enabled || m_current == Undefined)
|
||||
return;
|
||||
|
||||
@@ -435,7 +443,9 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
|
||||
else if (m_current == MmuSegmentation)
|
||||
return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Measure)
|
||||
return dynamic_cast<GLGizmoMeasure*>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
return dynamic_cast<GLGizmoMeasure *>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Assembly)
|
||||
return dynamic_cast<GLGizmoAssembly *>(m_gizmos[Assembly].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Cut)
|
||||
return dynamic_cast<GLGizmoCut3D*>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == MeshBoolean)
|
||||
@@ -656,6 +666,11 @@ bool GLGizmosManager::on_mouse(const wxMouseEvent &mouse_event)
|
||||
// &m_gizmos[m_current]->on_mouse != &GLGizmoBase::on_mouse &&
|
||||
m_gizmos[m_current]->on_mouse(mouse_event))
|
||||
return true;
|
||||
|
||||
if (mouse_event.RightUp() && m_current != EType::Undefined && !m_parent.is_mouse_dragging()) {
|
||||
// Prevent default right context menu in gizmos
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -693,7 +708,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
|
||||
case WXK_ESCAPE:
|
||||
{
|
||||
if (m_current != Undefined) {
|
||||
if (m_current == Measure && gizmo_event(SLAGizmoEventType::Escape)) {
|
||||
if ((m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Escape)) {
|
||||
// do nothing
|
||||
} else
|
||||
//if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges))
|
||||
@@ -703,14 +718,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WXK_BACK:
|
||||
case WXK_DELETE:
|
||||
{
|
||||
if ((m_current == Cut || m_current == Measure) && gizmo_event(SLAGizmoEventType::Delete))
|
||||
processed = true;
|
||||
|
||||
break;
|
||||
}
|
||||
//skip some keys when gizmo
|
||||
case 'A':
|
||||
case 'a':
|
||||
{
|
||||
@@ -737,14 +745,12 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
|
||||
//}
|
||||
|
||||
|
||||
//case WXK_BACK:
|
||||
//case WXK_DELETE:
|
||||
//{
|
||||
// if ((m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Delete))
|
||||
// processed = true;
|
||||
|
||||
// break;
|
||||
//}
|
||||
case WXK_BACK:
|
||||
case WXK_DELETE: {
|
||||
if ((m_current == Cut || m_current == Measure || m_current == Assembly) && gizmo_event(SLAGizmoEventType::Delete))
|
||||
processed = true;
|
||||
break;
|
||||
}
|
||||
//case 'A':
|
||||
//case 'a':
|
||||
//{
|
||||
@@ -839,13 +845,12 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
||||
processed = true;
|
||||
}
|
||||
}*/
|
||||
if (m_current == Measure) {
|
||||
if (m_current == Measure || m_current == Assembly) {
|
||||
if (keyCode == WXK_CONTROL)
|
||||
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
|
||||
else if (keyCode == WXK_SHIFT)
|
||||
gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
|
||||
}
|
||||
|
||||
// if (processed)
|
||||
// m_parent.set_cursor(GLCanvas3D::Standard);
|
||||
}
|
||||
@@ -927,8 +932,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
||||
// force extra frame to automatically update window size
|
||||
wxGetApp().imgui()->set_requires_extra_frame();
|
||||
}
|
||||
}
|
||||
else if (m_current == Measure) {
|
||||
} else if (m_current == Measure || m_current == Assembly) {
|
||||
if (keyCode == WXK_CONTROL)
|
||||
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
|
||||
else if (keyCode == WXK_SHIFT)
|
||||
|
||||
@@ -86,6 +86,7 @@ public:
|
||||
Emboss,
|
||||
Svg,
|
||||
Measure,
|
||||
Assembly,
|
||||
Simplify,
|
||||
//SlaSupports,
|
||||
// BBS
|
||||
|
||||
@@ -161,7 +161,9 @@ const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = COL_ORANGE_LIGHT;
|
||||
const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = COL_BUTTON_HOVERED;
|
||||
|
||||
//BBS
|
||||
|
||||
const ImVec4 ImGuiWrapper::COL_RED = ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
const ImVec4 ImGuiWrapper::COL_GREEN = ImVec4(0.0f, 1.0f, 0.0f, 1.0f);
|
||||
const ImVec4 ImGuiWrapper::COL_BLUE = ImVec4(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
const ImVec4 ImGuiWrapper::COL_BLUE_LIGHT = ImVec4(0.122f, 0.557f, 0.918f, 1.0f);
|
||||
const ImVec4 ImGuiWrapper::COL_GREEN_LIGHT = { 0.f, 156 / 255.f, 136 / 255.f, 0.25f }; // ORCA used on various places like text selection bg. Replaced with orca color
|
||||
const ImVec4 ImGuiWrapper::COL_HOVER = { 0.933f, 0.933f, 0.933f, 1.0f };
|
||||
@@ -856,6 +858,10 @@ bool ImGuiWrapper::radio_button(const wxString &label, bool active)
|
||||
return ImGui::RadioButton(label_utf8.c_str(), active);
|
||||
}
|
||||
|
||||
ImVec4 ImGuiWrapper::to_ImVec4(const ColorRGB &color) {
|
||||
return {color.r(), color.g(), color.b(), 1.0};
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
|
||||
{
|
||||
return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal);
|
||||
@@ -943,6 +949,19 @@ void ImGuiWrapper::text(const wxString &label)
|
||||
ImGuiWrapper::text(label_utf8.c_str());
|
||||
}
|
||||
|
||||
void ImGuiWrapper::warning_text(const char *label)
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiWrapper::to_ImVec4(ColorRGB::WARNING()));
|
||||
this->text(label);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
void ImGuiWrapper::warning_text(const wxString &all_text)
|
||||
{
|
||||
auto label_utf8 = into_u8(all_text);
|
||||
warning_text(label_utf8.c_str());
|
||||
}
|
||||
|
||||
void ImGuiWrapper::text_colored(const ImVec4& color, const char* label)
|
||||
{
|
||||
ImGui::TextColored(color, "%s", label);
|
||||
|
||||
@@ -134,6 +134,7 @@ public:
|
||||
bool button(const wxString& label, float width, float height);
|
||||
bool button(const wxString& label, const ImVec2 &size, bool enable); // default size = ImVec2(0.f, 0.f)
|
||||
bool radio_button(const wxString &label, bool active);
|
||||
static ImVec4 to_ImVec4(const ColorRGB &color);
|
||||
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
|
||||
bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f");
|
||||
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
|
||||
@@ -144,6 +145,8 @@ public:
|
||||
static void text(const char *label);
|
||||
static void text(const std::string &label);
|
||||
static void text(const wxString &label);
|
||||
void warning_text(const char *all_text);
|
||||
void warning_text(const wxString &all_text);
|
||||
static void text_colored(const ImVec4& color, const char* label);
|
||||
static void text_colored(const ImVec4& color, const std::string& label);
|
||||
static void text_colored(const ImVec4& color, const wxString& label);
|
||||
@@ -323,6 +326,9 @@ public:
|
||||
static const ImVec4 COL_BUTTON_ACTIVE;
|
||||
|
||||
//BBS add more colors
|
||||
static const ImVec4 COL_RED;
|
||||
static const ImVec4 COL_GREEN;
|
||||
static const ImVec4 COL_BLUE;
|
||||
static const ImVec4 COL_BLUE_LIGHT;
|
||||
static const ImVec4 COL_GREEN_LIGHT;
|
||||
static const ImVec4 COL_HOVER;
|
||||
|
||||
@@ -652,7 +652,8 @@ void Slic3r::GUI::ImageGrid::renderContent1(wxDC &dc, wxPoint const &pt, int ind
|
||||
if (hit) {
|
||||
texts.Add(_L("Delete"));
|
||||
texts.Add(secondAction);
|
||||
texts.Add(thirdAction);
|
||||
if (!thirdAction.IsEmpty())
|
||||
texts.Add(thirdAction);
|
||||
renderButtons(dc, texts, rect, m_hit_type == HIT_ACTION ? m_hit_item & 3 : -1, states);
|
||||
} else if (!nonHoverText.IsEmpty()) {
|
||||
texts.Add(nonHoverText);
|
||||
|
||||
@@ -29,7 +29,7 @@ static const std::vector<int> _3DCONNEXION_VENDORS =
|
||||
0x256F // 3DCONNECTION = 9583 // 3Dconnexion
|
||||
};
|
||||
|
||||
// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202
|
||||
// See: https://github.com/FreeSpacenav/spacenavd/blob/39856625a6de1e8c4b57c5938e1bf29d13cf1a9f/src/dev.c#L63
|
||||
static const std::vector<int> _3DCONNEXION_DEVICES =
|
||||
{
|
||||
0xc603, /* 50691 spacemouse plus XT */
|
||||
@@ -43,6 +43,7 @@ static const std::vector<int> _3DCONNEXION_DEVICES =
|
||||
0xc628, /* 50728 space navigator for notebooks*/
|
||||
0xc629, /* 50729 space pilot pro*/
|
||||
0xc62b, /* 50731 space mouse pro*/
|
||||
0xc640, /* 50752 nulooq */
|
||||
0xc62e, /* 50734 spacemouse wireless (USB cable) *TESTED* */
|
||||
0xc62f, /* 50735 spacemouse wireless receiver */
|
||||
0xc631, /* 50737 spacemouse pro wireless *TESTED* */
|
||||
@@ -50,7 +51,7 @@ static const std::vector<int> _3DCONNEXION_DEVICES =
|
||||
0xc633, /* 50739 spacemouse enterprise */
|
||||
0xc635, /* 50741 spacemouse compact *TESTED* */
|
||||
0xc636, /* 50742 spacemouse module */
|
||||
0xc640, /* 50752 nulooq */
|
||||
0xc63a, /* 60060 spacemouse wireless (Bluetooth) */
|
||||
0xc652, /* 50770 3Dconnexion universal receiver *TESTED* */
|
||||
};
|
||||
|
||||
|
||||
@@ -5499,7 +5499,9 @@ void PartPlateList::BedTextureInfo::reset()
|
||||
void PartPlateList::init_bed_type_info()
|
||||
{
|
||||
BedTextureInfo::TexturePart pct_part_left(10, 130, 10, 110, "orca_bed_pct_left.svg");
|
||||
BedTextureInfo::TexturePart pc_part1(10, 130, 10, 110, "bbl_bed_pc_left.svg");
|
||||
BedTextureInfo::TexturePart st_part1(9, 70, 12.5, 170, "bbl_bed_st_left.svg");
|
||||
BedTextureInfo::TexturePart st_part2(74, -10, 148, 12, "bbl_bed_st_bottom.svg");
|
||||
BedTextureInfo::TexturePart pc_part1(10, 130, 10, 110, "bbl_bed_pc_left.svg");
|
||||
BedTextureInfo::TexturePart pc_part2(74, -10, 148, 12, "bbl_bed_pc_bottom.svg");
|
||||
BedTextureInfo::TexturePart ep_part1(7.5, 90, 12.5, 150, "bbl_bed_ep_left.svg");
|
||||
BedTextureInfo::TexturePart ep_part2(74, -10, 148, 12, "bbl_bed_ep_bottom.svg");
|
||||
@@ -5511,6 +5513,8 @@ void PartPlateList::init_bed_type_info()
|
||||
bed_texture_info[i].reset();
|
||||
bed_texture_info[i].parts.clear();
|
||||
}
|
||||
bed_texture_info[btSuperTack].parts.push_back(st_part1);
|
||||
bed_texture_info[btSuperTack].parts.push_back(st_part2);
|
||||
bed_texture_info[btPC].parts.push_back(pc_part1);
|
||||
bed_texture_info[btPC].parts.push_back(pc_part2);
|
||||
bed_texture_info[btPCT].parts.push_back(pct_part_left);
|
||||
|
||||
@@ -10128,6 +10128,10 @@ void Plater::load_gcode(const wxString& filename)
|
||||
set_project_filename(filename);
|
||||
}
|
||||
|
||||
// Orca: Fix crash when loading gcode file multiple times
|
||||
if (m_only_gcode) {
|
||||
p->view3D->get_canvas3d()->remove_raycasters_for_picking(SceneRaycaster::EType::Bed);
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::reload_gcode_from_disk()
|
||||
@@ -12347,7 +12351,6 @@ void Plater::reslice()
|
||||
// Post the "complete" callback message, so that it will slice the next plate soon
|
||||
wxQueueEvent(this, evt.Clone());
|
||||
p->m_is_slicing = true;
|
||||
this->SetDropTarget(nullptr);
|
||||
if (p->m_cur_slice_plate == 0)
|
||||
reset_gcode_toolpaths();
|
||||
return;
|
||||
@@ -12355,7 +12358,6 @@ void Plater::reslice()
|
||||
|
||||
if (result) {
|
||||
p->m_is_slicing = true;
|
||||
this->SetDropTarget(nullptr);
|
||||
}
|
||||
|
||||
bool clean_gcode_toolpaths = true;
|
||||
|
||||
@@ -502,21 +502,20 @@ std::vector<std::string> UpdateVersionDialog::splitWithStl(std::string str,std::
|
||||
void UpdateVersionDialog::update_version_info(wxString release_note, wxString version)
|
||||
{
|
||||
//bbs check whether the web display is used
|
||||
bool use_web_link = false;
|
||||
url_line = "";
|
||||
auto split_array = splitWithStl(release_note.ToStdString(), "###");
|
||||
|
||||
if (split_array.size() >= 3) {
|
||||
for (auto i = 0; i < split_array.size(); i++) {
|
||||
std::string url = split_array[i];
|
||||
if (std::strstr(url.c_str(), "http://") != NULL || std::strstr(url.c_str(), "https://") != NULL) {
|
||||
use_web_link = true;
|
||||
url_line = url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool use_web_link = false;
|
||||
url_line = "";
|
||||
// Orca: not used in Orca Slicer
|
||||
// auto split_array = splitWithStl(release_note.ToStdString(), "###");
|
||||
// if (split_array.size() >= 3) {
|
||||
// for (auto i = 0; i < split_array.size(); i++) {
|
||||
// std::string url = split_array[i];
|
||||
// if (std::strstr(url.c_str(), "http://") != NULL || std::strstr(url.c_str(), "https://") != NULL) {
|
||||
// use_web_link = true;
|
||||
// url_line = url;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (use_web_link) {
|
||||
m_brand->Hide();
|
||||
|
||||
@@ -21,6 +21,9 @@ class SceneRaycasterItem
|
||||
Transform3d m_trafo;
|
||||
|
||||
public:
|
||||
SceneRaycasterItem(int id, const MeshRaycaster& raycaster)
|
||||
: m_id(id), m_raycaster(&raycaster), m_trafo(Transform3d::Identity()), m_use_back_faces(false)
|
||||
{}
|
||||
SceneRaycasterItem(int id, const MeshRaycaster& raycaster, const Transform3d& trafo, bool use_back_faces = false)
|
||||
: m_id(id), m_raycaster(&raycaster), m_trafo(trafo), m_use_back_faces(use_back_faces)
|
||||
{}
|
||||
|
||||
@@ -1720,6 +1720,66 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co
|
||||
this->set_bounding_boxes_dirty();
|
||||
}
|
||||
|
||||
void Selection::translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement) {
|
||||
if (!m_valid) return;
|
||||
|
||||
for (unsigned int i : m_list) {
|
||||
GLVolume &v = *(*m_volumes)[i];
|
||||
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx && v.volume_idx() == (int) volume_idx)
|
||||
v.set_volume_offset(v.get_volume_offset() + displacement);
|
||||
}
|
||||
|
||||
this->set_bounding_boxes_dirty();
|
||||
}
|
||||
|
||||
void Selection::rotate(unsigned int object_idx, unsigned int instance_idx, const Transform3d &overwrite_tran)
|
||||
{
|
||||
if (!m_valid) return;
|
||||
|
||||
for (unsigned int i : m_list) {
|
||||
GLVolume &v = *(*m_volumes)[i];
|
||||
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx) {
|
||||
v.set_instance_transformation(overwrite_tran);
|
||||
}
|
||||
}
|
||||
|
||||
std::set<unsigned int> done; // prevent processing volumes twice
|
||||
done.insert(m_list.begin(), m_list.end());
|
||||
for (unsigned int i : m_list) {
|
||||
if (done.size() == m_volumes->size()) break;
|
||||
|
||||
int object_idx = (*m_volumes)[i]->object_idx();
|
||||
if (object_idx >= 1000) continue;
|
||||
|
||||
// Process unselected volumes of the object.
|
||||
for (unsigned int j = 0; j < (unsigned int) m_volumes->size(); ++j) {
|
||||
if (done.size() == m_volumes->size()) break;
|
||||
|
||||
if (done.find(j) != done.end()) continue;
|
||||
|
||||
GLVolume &v = *(*m_volumes)[j];
|
||||
if (v.object_idx() != object_idx || v.instance_idx() != (int) instance_idx)
|
||||
continue;
|
||||
|
||||
v.set_instance_transformation(overwrite_tran);
|
||||
done.insert(j);
|
||||
}
|
||||
}
|
||||
this->set_bounding_boxes_dirty();
|
||||
}
|
||||
void Selection::rotate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Transform3d &overwrite_tran)
|
||||
{
|
||||
if (!m_valid) return;
|
||||
|
||||
for (unsigned int i : m_list) {
|
||||
GLVolume &v = *(*m_volumes)[i];
|
||||
if (v.object_idx() == (int) object_idx && v.instance_idx() == (int) instance_idx && v.volume_idx() == (int) volume_idx) {
|
||||
v.set_volume_transformation(overwrite_tran);
|
||||
}
|
||||
}
|
||||
this->set_bounding_boxes_dirty();
|
||||
}
|
||||
|
||||
//BBS: add partplate related logic
|
||||
void Selection::notify_instance_update(int object_idx, int instance_idx)
|
||||
{
|
||||
|
||||
@@ -344,6 +344,10 @@ public:
|
||||
|
||||
void translate(unsigned int object_idx, const Vec3d& displacement);
|
||||
void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement);
|
||||
void translate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Vec3d &displacement);
|
||||
|
||||
void rotate(unsigned int object_idx, unsigned int instance_idx, const Transform3d &overwrite_tran);
|
||||
void rotate(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx, const Transform3d &overwrite_tran);
|
||||
//BBS: add partplate related logic
|
||||
void notify_instance_update(int object_idx, int instance_idx);
|
||||
// BBS
|
||||
|
||||
+42
-16
@@ -1649,7 +1649,14 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
//Orca: sync filament num if it's a multi tool printer
|
||||
if (opt_key == "extruders_count" && !m_config->opt_bool("single_extruder_multi_material")){
|
||||
auto num_extruder = boost::any_cast<size_t>(value);
|
||||
wxGetApp().preset_bundle->set_num_filaments(num_extruder);
|
||||
int old_filament_size = wxGetApp().preset_bundle->filament_presets.size();
|
||||
std::vector<std::string> new_colors;
|
||||
for (int i = old_filament_size; i < num_extruder; ++i) {
|
||||
wxColour new_col = Plater::get_next_color_for_filament();
|
||||
std::string new_color = new_col.GetAsString(wxC2S_HTML_SYNTAX).ToStdString();
|
||||
new_colors.push_back(new_color);
|
||||
}
|
||||
wxGetApp().preset_bundle->set_num_filaments(num_extruder, new_colors);
|
||||
wxGetApp().plater()->on_filaments_change(num_extruder);
|
||||
wxGetApp().get_tab(Preset::TYPE_PRINT)->update();
|
||||
wxGetApp().preset_bundle->export_selections(*wxGetApp().app_config);
|
||||
@@ -2116,19 +2123,19 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("detect_thin_wall");
|
||||
|
||||
optgroup = page->new_optgroup(L("Top/bottom shells"), L"param_shell");
|
||||
optgroup->append_single_option_line("top_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface");
|
||||
optgroup->append_single_option_line("top_shell_layers");
|
||||
optgroup->append_single_option_line("top_shell_thickness");
|
||||
optgroup->append_single_option_line("bottom_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface");
|
||||
optgroup->append_single_option_line("top_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface");
|
||||
optgroup->append_single_option_line("bottom_shell_layers");
|
||||
optgroup->append_single_option_line("bottom_shell_thickness");
|
||||
optgroup->append_single_option_line("bottom_surface_pattern", "fill-patterns#Infill of the top surface and bottom surface");
|
||||
optgroup->append_single_option_line("top_bottom_infill_wall_overlap");
|
||||
|
||||
optgroup = page->new_optgroup(L("Infill"), L"param_infill");
|
||||
optgroup->append_single_option_line("sparse_infill_density");
|
||||
optgroup->append_single_option_line("sparse_infill_pattern", "fill-patterns#infill types and their properties of sparse");
|
||||
optgroup->append_single_option_line("infill_anchor");
|
||||
optgroup->append_single_option_line("infill_anchor_max");
|
||||
optgroup->append_single_option_line("infill_anchor");
|
||||
optgroup->append_single_option_line("internal_solid_infill_pattern");
|
||||
optgroup->append_single_option_line("gap_fill_target");
|
||||
optgroup->append_single_option_line("filter_out_gap_fill");
|
||||
@@ -2313,8 +2320,8 @@ void TabPrint::build()
|
||||
|
||||
page = add_options_page(L("Others"), "custom-gcode_other"); // ORCA: icon only visible on placeholders
|
||||
optgroup = page->new_optgroup(L("Skirt"), L"param_skirt");
|
||||
optgroup->append_single_option_line("skirt_type");
|
||||
optgroup->append_single_option_line("skirt_loops");
|
||||
optgroup->append_single_option_line("skirt_type");
|
||||
optgroup->append_single_option_line("min_skirt_length");
|
||||
optgroup->append_single_option_line("skirt_distance");
|
||||
optgroup->append_single_option_line("skirt_start_angle");
|
||||
@@ -3328,7 +3335,12 @@ void TabFilament::build()
|
||||
optgroup->append_line(line);
|
||||
|
||||
optgroup = page->new_optgroup(L("Bed temperature"), L"param_bed_temp");
|
||||
line = { L("Cool plate"), L("Bed temperature when cool plate is installed. Value 0 means the filament does not support to print on the Cool Plate") };
|
||||
line = {L("Bambu Cool Plate SuperTack"), L("Bed temperature when cool plate is installed. Value 0 means the filament does not support to print on the Bambu Cool Plate SuperTack")};
|
||||
line.append_option(optgroup->get_option("supertack_plate_temp_initial_layer"));
|
||||
line.append_option(optgroup->get_option("supertack_plate_temp"));
|
||||
optgroup->append_line(line);
|
||||
|
||||
line = { L("Cool Plate / PLA Plate"), L("Bed temperature when cool plate is installed. Value 0 means the filament does not support to print on the Cool Plate") };
|
||||
line.append_option(optgroup->get_option("cool_plate_temp_initial_layer"));
|
||||
line.append_option(optgroup->get_option("cool_plate_temp"));
|
||||
optgroup->append_line(line);
|
||||
@@ -3618,6 +3630,12 @@ void TabFilament::toggle_options()
|
||||
bool is_pellet_printer = cfg.opt_bool("pellet_modded_printer");
|
||||
toggle_line("pellet_flow_coefficient", is_pellet_printer);
|
||||
toggle_line("filament_diameter", !is_pellet_printer);
|
||||
|
||||
bool support_chamber_temp_control = this->m_preset_bundle->printers.get_edited_preset().config.opt_bool("support_chamber_temp_control");
|
||||
toggle_line("chamber_temperatures", support_chamber_temp_control);
|
||||
|
||||
for (auto el : {"supertack_plate_temp", "supertack_plate_temp_initial_layer", "cool_plate_temp", "cool_plate_temp_initial_layer", "eng_plate_temp", "eng_plate_temp_initial_layer", "textured_plate_temp", "textured_plate_temp_initial_layer"})
|
||||
toggle_line(el, is_BBL_printer);
|
||||
}
|
||||
if (m_active_page->title() == L("Setting Overrides"))
|
||||
update_filament_overrides_page(&cfg);
|
||||
@@ -4315,21 +4333,22 @@ if (is_marlin_flavor)
|
||||
optgroup = page->new_optgroup(L("Retraction"), L"param_retraction");
|
||||
optgroup->append_single_option_line("retraction_length", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_restart_extra", "", extruder_idx);
|
||||
optgroup->append_single_option_line("z_hop", "", extruder_idx);
|
||||
optgroup->append_single_option_line("z_hop_types", "", extruder_idx);
|
||||
optgroup->append_single_option_line("travel_slope", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retraction_speed", "", extruder_idx);
|
||||
optgroup->append_single_option_line("deretraction_speed", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retraction_minimum_travel", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_when_changing_layer", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_on_top_layer", "", extruder_idx);
|
||||
optgroup->append_single_option_line("wipe", "", extruder_idx);
|
||||
optgroup->append_single_option_line("wipe_distance", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_before_wipe", "", extruder_idx);
|
||||
|
||||
optgroup = page->new_optgroup(L("Lift Z Enforcement"), L"param_extruder_lift_enforcement");
|
||||
optgroup = page->new_optgroup(L("Z-Hop"), L"param_extruder_lift_enforcement");
|
||||
optgroup->append_single_option_line("retract_lift_enforce", "", extruder_idx);
|
||||
optgroup->append_single_option_line("z_hop_types", "", extruder_idx);
|
||||
optgroup->append_single_option_line("z_hop", "", extruder_idx);
|
||||
optgroup->append_single_option_line("travel_slope", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_lift_above", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_lift_below", "", extruder_idx);
|
||||
optgroup->append_single_option_line("retract_lift_enforce", "", extruder_idx);
|
||||
|
||||
optgroup = page->new_optgroup(L("Retraction when switching material"), L"param_retraction_material_change");
|
||||
optgroup->append_single_option_line("retract_length_toolchange", "", extruder_idx);
|
||||
@@ -4538,7 +4557,7 @@ void TabPrinter::toggle_options()
|
||||
// user can customize other retraction options if retraction is enabled
|
||||
//BBS
|
||||
bool retraction = have_retract_length || use_firmware_retraction;
|
||||
std::vector<std::string> vec = { "z_hop", "retract_when_changing_layer" };
|
||||
std::vector<std::string> vec = {"z_hop", "retract_when_changing_layer", "retract_on_top_layer"};
|
||||
for (auto el : vec)
|
||||
toggle_option(el, retraction, i);
|
||||
|
||||
@@ -5096,8 +5115,14 @@ bool Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
|
||||
|
||||
// Orca: update presets for the selected printer
|
||||
if (m_type == Preset::TYPE_PRINTER && wxGetApp().app_config->get_bool("remember_printer_config")) {
|
||||
m_preset_bundle->update_selections(*wxGetApp().app_config);
|
||||
wxGetApp().plater()->sidebar().on_filaments_change(m_preset_bundle->filament_presets.size());
|
||||
m_preset_bundle->update_selections(*wxGetApp().app_config);
|
||||
int extruders_count = m_preset_bundle->printers.get_edited_preset().config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||
if (extruders_count > 1) {
|
||||
// multi tool
|
||||
wxGetApp().plater()->sidebar().on_filaments_change(extruders_count);
|
||||
} else {
|
||||
wxGetApp().plater()->sidebar().on_filaments_change(m_preset_bundle->filament_presets.size());
|
||||
}
|
||||
}
|
||||
load_current_preset();
|
||||
|
||||
@@ -6079,8 +6104,9 @@ void Page::update_visibility(ConfigOptionMode mode, bool update_contolls_visibil
|
||||
#ifdef __WXMSW__
|
||||
if (!m_show) return;
|
||||
// BBS: fix field control position
|
||||
wxTheApp->CallAfter([this]() {
|
||||
for (auto group : m_optgroups) {
|
||||
auto groups = this->m_optgroups;
|
||||
wxTheApp->CallAfter([groups]() {
|
||||
for (auto group : groups) {
|
||||
if (group->custom_ctrl) group->custom_ctrl->fixup_items_positions();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1664,17 +1664,19 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres
|
||||
|
||||
// process changes of extruders count
|
||||
if (type == Preset::TYPE_PRINTER && old_pt == ptFFF &&
|
||||
old_config.opt<ConfigOptionStrings>("extruder_colour")->values.size() != new_config.opt<ConfigOptionStrings>("extruder_colour")->values.size()) {
|
||||
old_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size() != new_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size()) {
|
||||
wxString local_label = _L("Extruders count");
|
||||
wxString old_val = from_u8((boost::format("%1%") % old_config.opt<ConfigOptionStrings>("extruder_colour")->values.size()).str());
|
||||
wxString new_val = from_u8((boost::format("%1%") % new_config.opt<ConfigOptionStrings>("extruder_colour")->values.size()).str());
|
||||
wxString old_val = from_u8((boost::format("%1%") % old_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size()).str());
|
||||
wxString new_val = from_u8((boost::format("%1%") % new_config.opt<ConfigOptionFloats>("nozzle_diameter")->values.size()).str());
|
||||
|
||||
//BBS: the page "General" changed to "Basic information" instead
|
||||
//m_tree->Append("extruders_count", type, _L("General"), _L("Capabilities"), local_label, old_val, new_val, category_icon_map.at("Basic information"));
|
||||
//m_tree->Append("extruders_count", type, _L("General"), _L("Capabilities"), local_label, old_val, new_val, category_icon_map.at("General"));
|
||||
|
||||
PresetItem pi = {type, "extruders_count", _L("General"), _L("Capabilities"), local_label, old_val, new_val};
|
||||
m_presetitems.push_back(pi);
|
||||
if (old_val != new_val) {
|
||||
PresetItem pi = {type, "extruders_count", _L("General"), _L("Capabilities"), local_label, old_val, new_val};
|
||||
m_presetitems.push_back(pi);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& opt_key : dirty_options) {
|
||||
@@ -1822,11 +1824,11 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString
|
||||
static PresetCollection* get_preset_collection(Preset::Type type, PresetBundle* preset_bundle = nullptr) {
|
||||
if (!preset_bundle)
|
||||
preset_bundle = wxGetApp().preset_bundle;
|
||||
return type == Preset::Type::TYPE_PRINT ? &preset_bundle->prints :
|
||||
type == Preset::Type::TYPE_SLA_PRINT ? &preset_bundle->sla_prints :
|
||||
return type == Preset::Type::TYPE_PRINTER ? &preset_bundle->printers :
|
||||
type == Preset::Type::TYPE_FILAMENT ? &preset_bundle->filaments :
|
||||
type == Preset::Type::TYPE_SLA_MATERIAL ? &preset_bundle->sla_materials :
|
||||
type == Preset::Type::TYPE_PRINTER ? &preset_bundle->printers :
|
||||
type == Preset::Type::TYPE_PRINT ? &preset_bundle->prints :
|
||||
type == Preset::Type::TYPE_SLA_PRINT ? &preset_bundle->sla_prints :
|
||||
nullptr;
|
||||
}
|
||||
|
||||
@@ -1842,7 +1844,7 @@ void DiffPresetDialog::create_presets_sizer()
|
||||
{
|
||||
m_presets_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
for (auto new_type : { Preset::TYPE_PRINT, Preset::TYPE_SLA_PRINT, Preset::TYPE_FILAMENT, Preset::TYPE_SLA_MATERIAL, Preset::TYPE_PRINTER })
|
||||
for (auto new_type : { Preset::TYPE_PRINTER, Preset::TYPE_FILAMENT, Preset::TYPE_SLA_MATERIAL, Preset::TYPE_PRINT, Preset::TYPE_SLA_PRINT })
|
||||
{
|
||||
const PresetCollection* collection = get_preset_collection(new_type);
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
@@ -312,6 +312,8 @@ public:
|
||||
{
|
||||
}
|
||||
};
|
||||
public:
|
||||
int getUpdateItemCount() { return m_presetitems.size(); }
|
||||
|
||||
private:
|
||||
std::vector<PresetItem> m_presetitems;
|
||||
|
||||
@@ -628,7 +628,7 @@ void CalibUtils::calib_pa_pattern(const CalibInfo &calib_info, Model& model)
|
||||
print_config.get_abs_value("layer_height"), 0)));
|
||||
|
||||
for (const auto& opt : SuggestedConfigCalibPAPattern().nozzle_ratio_pairs) {
|
||||
print_config.set_key_value(opt.first, new ConfigOptionFloat(nozzle_diameter * opt.second / 100));
|
||||
print_config.set_key_value(opt.first, new ConfigOptionFloatOrPercent(nozzle_diameter * opt.second / 100, false));
|
||||
}
|
||||
|
||||
for (const auto& opt : SuggestedConfigCalibPAPattern().int_pairs) {
|
||||
|
||||
Reference in new issue
Block a user