Merge branch 'main' into bugfox/layer-cooling

This commit is contained in:
SoftFever authored and GitHub committed 2024-12-25 23:43:09 +08:00
commit f3b2dc26f5
778 files changed
+18275 -4773

No files matched your search

+23 -7
View File
@@ -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,
+1 -1
View File
@@ -264,7 +264,7 @@ SplittedLine do_split_line(const ClipperZUtils::ZPath& path, const ExPolygons& c
}
for (const auto segment : node) {
for (const ClipperZUtils::ZPoint& sp : *segment) {
assert(!is_clip(sp.z()));
assert(!is_clip(sp));
result.emplace_back(to_point(sp), true, sp.z());
}
result.back().clipped = false; // Mark the end of the clipped line
-128
View File
@@ -1,128 +0,0 @@
#ifndef SRC_LIBSLIC3R_PATH_SORTING_HPP_
#define SRC_LIBSLIC3R_PATH_SORTING_HPP_
#include "AABBTreeLines.hpp"
#include "BoundingBox.hpp"
#include "Line.hpp"
#include "ankerl/unordered_dense.h"
#include <algorithm>
#include <iterator>
#include <libslic3r/Point.hpp>
#include <libslic3r/Polygon.hpp>
#include <libslic3r/ExPolygon.hpp>
#include <limits>
#include <type_traits>
#include <unordered_set>
namespace Slic3r {
namespace Algorithm {
//Sorts the paths such that all paths between begin and last_seed are printed first, in some order. The rest of the paths is sorted
// such that the paths that are touching some of the already printed are printed first, sorted secondary by the distance to the last point of the last
// printed path.
// begin, end, and last_seed are random access iterators. touch_limit_distance is used to check if the paths are touching - if any part of the path gets this close
// to the second, then they touch.
// convert_to_lines is a lambda that should accept the path as argument and return it as Lines vector, in correct order.
template<typename RandomAccessIterator, typename ToLines>
void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point start, double touch_limit_distance, ToLines convert_to_lines)
{
size_t paths_count = std::distance(begin, end);
if (paths_count <= 1)
return;
auto paths_touch = [touch_limit_distance](const AABBTreeLines::LinesDistancer<Line> &left,
const AABBTreeLines::LinesDistancer<Line> &right) {
for (const Line &l : left.get_lines()) {
if (right.distance_from_lines<false>(l.a) < touch_limit_distance) {
return true;
}
}
if (right.distance_from_lines<false>(left.get_lines().back().b) < touch_limit_distance) {
return true;
}
for (const Line &l : right.get_lines()) {
if (left.distance_from_lines<false>(l.a) < touch_limit_distance) {
return true;
}
}
if (left.distance_from_lines<false>(right.get_lines().back().b) < touch_limit_distance) {
return true;
}
return false;
};
std::vector<AABBTreeLines::LinesDistancer<Line>> distancers(paths_count);
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
distancers[path_idx] = AABBTreeLines::LinesDistancer<Line>{convert_to_lines(*std::next(begin, path_idx))};
}
std::vector<std::unordered_set<size_t>> dependencies(paths_count);
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
for (size_t next_path_idx = path_idx + 1; next_path_idx < paths_count; next_path_idx++) {
if (paths_touch(distancers[path_idx], distancers[next_path_idx])) {
dependencies[next_path_idx].insert(path_idx);
}
}
}
Point current_point = start;
std::vector<std::pair<size_t, bool>> correct_order_and_direction(paths_count);
size_t unsorted_idx = 0;
size_t null_idx = size_t(-1);
size_t next_idx = null_idx;
bool reverse = false;
while (unsorted_idx < paths_count) {
next_idx = null_idx;
double lines_dist = std::numeric_limits<double>::max();
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
if (!dependencies[path_idx].empty())
continue;
double ldist = distancers[path_idx].distance_from_lines<false>(current_point);
if (ldist < lines_dist) {
const auto &lines = distancers[path_idx].get_lines();
double dist_a = (lines.front().a - current_point).cast<double>().squaredNorm();
double dist_b = (lines.back().b - current_point).cast<double>().squaredNorm();
next_idx = path_idx;
reverse = dist_b < dist_a;
lines_dist = ldist;
}
}
// we have valid next_idx, sort it, update dependencies, update current point
correct_order_and_direction[next_idx] = {unsorted_idx, reverse};
unsorted_idx++;
current_point = reverse ? distancers[next_idx].get_lines().front().a : distancers[next_idx].get_lines().back().b;
dependencies[next_idx].insert(null_idx); // prevent it from being selected again
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
dependencies[path_idx].erase(next_idx);
}
}
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
if (correct_order_and_direction[path_idx].second) {
std::next(begin, path_idx)->reverse();
}
}
for (size_t i = 0; i < correct_order_and_direction.size() - 1; i++) {
bool swapped = false;
for (size_t j = 0; j < correct_order_and_direction.size() - i - 1; j++) {
if (correct_order_and_direction[j].first > correct_order_and_direction[j + 1].first) {
std::swap(correct_order_and_direction[j], correct_order_and_direction[j + 1]);
std::iter_swap(std::next(begin, j), std::next(begin, j + 1));
swapped = true;
}
}
if (swapped == false) {
break;
}
}
}
}} // namespace Slic3r::Algorithm
#endif /*SRC_LIBSLIC3R_PATH_SORTING_HPP_*/
+11 -2
View File
@@ -153,10 +153,19 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
// ^ current
Point intersection_point;
bool has_intersection = Line(previous_previous.p, previous.p).intersection_infinite(Line(current.p, next.p), &intersection_point);
const auto dist_greater = [](const Point& p1, const Point& p2, const int64_t threshold) {
const auto vec = (p1 - p2).cwiseAbs().cast<uint64_t>();
if(vec.x() > threshold || vec.y() > threshold) {
// If this condition is true, the distance is definitely greater than the threshold.
// We don't need to calculate the squared norm at all, which avoid potential arithmetic overflow.
return true;
}
return vec.squaredNorm() > threshold;
};
if (!has_intersection
|| Line::distance_to_infinite_squared(intersection_point, previous.p, current.p) > double(allowed_error_distance_squared)
|| (intersection_point - previous.p).cast<int64_t>().squaredNorm() > smallest_line_segment_squared // The intersection point is way too far from the 'previous'
|| (intersection_point - current.p).cast<int64_t>().squaredNorm() > smallest_line_segment_squared) // and 'current' points, so it shouldn't replace 'current'
|| dist_greater(intersection_point, previous.p, smallest_line_segment_squared) // The intersection point is way too far from the 'previous'
|| dist_greater(intersection_point, current.p, smallest_line_segment_squared)) // and 'current' points, so it shouldn't replace 'current'
{
// We can't find a better spot for it, but the size of the line is more than 5 micron.
// So the only thing we can do here is leave it in...
+1 -6
View File
@@ -32,7 +32,6 @@ set(lisbslic3r_sources
AABBMesh.cpp
Algorithm/LineSplit.hpp
Algorithm/LineSplit.cpp
Algorithm/PathSorting.hpp
Algorithm/RegionExpansion.hpp
Algorithm/RegionExpansion.cpp
AnyPtr.hpp
@@ -504,7 +503,7 @@ if (_opts)
target_compile_options(libslic3r_cgal PRIVATE "${_opts_bad}")
endif()
target_link_libraries(libslic3r_cgal PRIVATE ${_cgal_tgt} libigl mcut)
target_link_libraries(libslic3r_cgal PRIVATE ${_cgal_tgt} libigl mcut boost_libs)
if (MSVC AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") # 32 bit MSVC workaround
target_compile_definitions(libslic3r_cgal PRIVATE CGAL_DO_NOT_USE_MPZF)
@@ -580,10 +579,6 @@ target_link_libraries(libslic3r
opencv_world
)
if(NOT SLIC3R_STATIC)
target_compile_definitions(libslic3r PUBLIC BOOST_ALL_DYN_LINK)
endif()
if(NOT WIN32)
target_link_libraries(libslic3r freetype)
if (NOT APPLE)
+1
View File
@@ -67,6 +67,7 @@ public:
static const ColorRGB YELLOW() { return { 1.0f, 1.0f, 0.0f }; }
static const ColorRGB WHITE() { return { 1.0f, 1.0f, 1.0f }; }
static const ColorRGB ORCA() { return {0.0f, 150.f / 255.0f, 136.0f / 255}; }
static const ColorRGB WARNING() { return {241.0f / 255, 117.f / 255.0f, 78.0f / 255}; }
static const ColorRGB X() { return { 0.75f, 0.0f, 0.0f }; }
static const ColorRGB Y() { return { 0.0f, 0.75f, 0.0f }; }
+254 -93
View File
@@ -130,7 +130,252 @@ struct SurfaceFill {
// Detect narrow infill regions
// Based on the anti-vibration algorithm from PrusaSlicer:
// https://github.com/prusa3d/PrusaSlicer/blob/94290e09d75f23719c3d2ab2398737c8be4c3fd6/src/libslic3r/Fill/FillEnsuring.cpp#L100-L289
// https://github.com/prusa3d/PrusaSlicer/blob/5dc04b4e8f14f65bbcc5377d62cad3e86c2aea36/src/libslic3r/Fill/FillEnsuring.cpp#L37-L273
static coord_t _MAX_LINE_LENGTH_TO_FILTER() // 4 mm.
{
return scaled<coord_t>(4.);
}
const constexpr size_t MAX_SKIPS_ALLOWED = 2; // Skip means propagation through long line.
const constexpr size_t MIN_DEPTH_FOR_LINE_REMOVING = 5;
struct LineNode
{
struct State
{
// The total number of long lines visited before this node was reached.
// We just need the minimum number of all possible paths to decide whether we can remove the line or not.
int min_skips_taken = 0;
// The total number of short lines visited before this node was reached.
int total_short_lines = 0;
// Some initial line is touching some long line. This information is propagated to neighbors.
bool initial_touches_long_lines = false;
bool initialized = false;
void reset() {
this->min_skips_taken = 0;
this->total_short_lines = 0;
this->initial_touches_long_lines = false;
this->initialized = false;
}
};
explicit LineNode(const Line &line) : line(line) {}
Line line;
// Pointers to line nodes in the previous and the next section that overlap with this line.
std::vector<LineNode*> next_section_overlapping_lines;
std::vector<LineNode*> prev_section_overlapping_lines;
bool is_removed = false;
State state;
// Return true if some initial line is touching some long line and this information was propagated into the current line.
bool is_initial_line_touching_long_lines() const {
if (prev_section_overlapping_lines.empty())
return false;
for (LineNode *line_node : prev_section_overlapping_lines) {
if (line_node->state.initial_touches_long_lines)
return true;
}
return false;
}
// Return true if the current line overlaps with some long line in the previous section.
bool is_touching_long_lines_in_previous_layer() const {
if (prev_section_overlapping_lines.empty())
return false;
const auto MAX_LINE_LENGTH_TO_FILTER = _MAX_LINE_LENGTH_TO_FILTER();
for (LineNode *line_node : prev_section_overlapping_lines) {
if (!line_node->is_removed && line_node->line.length() >= MAX_LINE_LENGTH_TO_FILTER)
return true;
}
return false;
}
// Return true if the current line overlaps with some line in the next section.
bool has_next_layer_neighbours() const {
if (next_section_overlapping_lines.empty())
return false;
for (LineNode *line_node : next_section_overlapping_lines) {
if (!line_node->is_removed)
return true;
}
return false;
}
};
using LineNodes = std::vector<LineNode>;
inline bool are_lines_overlapping_in_y_axes(const Line &first_line, const Line &second_line) {
return (second_line.a.y() <= first_line.a.y() && first_line.a.y() <= second_line.b.y())
|| (second_line.a.y() <= first_line.b.y() && first_line.b.y() <= second_line.b.y())
|| (first_line.a.y() <= second_line.a.y() && second_line.a.y() <= first_line.b.y())
|| (first_line.a.y() <= second_line.b.y() && second_line.b.y() <= first_line.b.y());
}
bool can_line_note_be_removed(const LineNode &line_node) {
const auto MAX_LINE_LENGTH_TO_FILTER = _MAX_LINE_LENGTH_TO_FILTER();
return (line_node.line.length() < MAX_LINE_LENGTH_TO_FILTER)
&& (line_node.state.total_short_lines > int(MIN_DEPTH_FOR_LINE_REMOVING)
|| (!line_node.is_initial_line_touching_long_lines() && !line_node.has_next_layer_neighbours()));
}
// Remove the node and propagate its removal to the previous sections.
void propagate_line_node_remove(const LineNode &line_node) {
std::queue<LineNode *> line_node_queue;
for (LineNode *prev_line : line_node.prev_section_overlapping_lines) {
if (prev_line->is_removed)
continue;
line_node_queue.emplace(prev_line);
}
for (; !line_node_queue.empty(); line_node_queue.pop()) {
LineNode &line_to_check = *line_node_queue.front();
if (can_line_note_be_removed(line_to_check)) {
line_to_check.is_removed = true;
for (LineNode *prev_line : line_to_check.prev_section_overlapping_lines) {
if (prev_line->is_removed)
continue;
line_node_queue.emplace(prev_line);
}
}
}
}
// Filter out short extrusions that could create vibrations.
static std::vector<Lines> filter_vibrating_extrusions(const std::vector<Lines> &lines_sections) {
// Initialize all line nodes.
std::vector<LineNodes> line_nodes_sections(lines_sections.size());
for (const Lines &lines_section : lines_sections) {
const size_t section_idx = &lines_section - lines_sections.data();
line_nodes_sections[section_idx].reserve(lines_section.size());
for (const Line &line : lines_section) {
line_nodes_sections[section_idx].emplace_back(line);
}
}
// Precalculate for each line node which line nodes in the previous and next section this line node overlaps.
for (auto curr_lines_section_it = line_nodes_sections.begin(); curr_lines_section_it != line_nodes_sections.end(); ++curr_lines_section_it) {
if (curr_lines_section_it != line_nodes_sections.begin()) {
const auto prev_lines_section_it = std::prev(curr_lines_section_it);
for (LineNode &curr_line : *curr_lines_section_it) {
for (LineNode &prev_line : *prev_lines_section_it) {
if (are_lines_overlapping_in_y_axes(curr_line.line, prev_line.line)) {
curr_line.prev_section_overlapping_lines.emplace_back(&prev_line);
}
}
}
}
if (std::next(curr_lines_section_it) != line_nodes_sections.end()) {
const auto next_lines_section_it = std::next(curr_lines_section_it);
for (LineNode &curr_line : *curr_lines_section_it) {
for (LineNode &next_line : *next_lines_section_it) {
if (are_lines_overlapping_in_y_axes(curr_line.line, next_line.line)) {
curr_line.next_section_overlapping_lines.emplace_back(&next_line);
}
}
}
}
}
const auto MAX_LINE_LENGTH_TO_FILTER = _MAX_LINE_LENGTH_TO_FILTER();
// Select each section as the initial lines section and propagate line node states from this initial lines section to the last lines section.
// During this propagation, we remove those lines that meet the conditions for its removal.
// When some line is removed, we propagate this removal to previous layers.
for (size_t initial_line_section_idx = 0; initial_line_section_idx < line_nodes_sections.size(); ++initial_line_section_idx) {
// Stars from non-removed short lines.
for (LineNode &initial_line : line_nodes_sections[initial_line_section_idx]) {
if (initial_line.is_removed || initial_line.line.length() >= MAX_LINE_LENGTH_TO_FILTER)
continue;
initial_line.state.reset();
initial_line.state.total_short_lines = 1;
initial_line.state.initial_touches_long_lines = initial_line.is_touching_long_lines_in_previous_layer();
initial_line.state.initialized = true;
}
// Iterate from the initial lines section until the last lines section.
for (size_t propagation_line_section_idx = initial_line_section_idx; propagation_line_section_idx < line_nodes_sections.size(); ++propagation_line_section_idx) {
// Before we propagate node states into next lines sections, we reset the state of all line nodes in the next line section.
if (propagation_line_section_idx + 1 < line_nodes_sections.size()) {
for (LineNode &propagation_line : line_nodes_sections[propagation_line_section_idx + 1]) {
propagation_line.state.reset();
}
}
for (LineNode &propagation_line : line_nodes_sections[propagation_line_section_idx]) {
if (propagation_line.is_removed || !propagation_line.state.initialized)
continue;
for (LineNode *neighbour_line : propagation_line.next_section_overlapping_lines) {
if (neighbour_line->is_removed)
continue;
const bool is_short_line = neighbour_line->line.length() < MAX_LINE_LENGTH_TO_FILTER;
const bool is_skip_allowed = propagation_line.state.min_skips_taken < int(MAX_SKIPS_ALLOWED);
if (!is_short_line && !is_skip_allowed)
continue;
const int neighbour_total_short_lines = propagation_line.state.total_short_lines + int(is_short_line);
const int neighbour_min_skips_taken = propagation_line.state.min_skips_taken + int(!is_short_line);
if (neighbour_line->state.initialized) {
// When the state of the node was previously filled, then we need to update data in such a way
// that will maximize the possibility of removing this node.
neighbour_line->state.min_skips_taken = std::max(neighbour_line->state.min_skips_taken, neighbour_total_short_lines);
neighbour_line->state.min_skips_taken = std::min(neighbour_line->state.min_skips_taken, neighbour_min_skips_taken);
// We will keep updating neighbor initial_touches_long_lines until it is equal to false.
if (neighbour_line->state.initial_touches_long_lines) {
neighbour_line->state.initial_touches_long_lines = propagation_line.state.initial_touches_long_lines;
}
} else {
neighbour_line->state.total_short_lines = neighbour_total_short_lines;
neighbour_line->state.min_skips_taken = neighbour_min_skips_taken;
neighbour_line->state.initial_touches_long_lines = propagation_line.state.initial_touches_long_lines;
neighbour_line->state.initialized = true;
}
}
if (can_line_note_be_removed(propagation_line)) {
// Remove the current node and propagate its removal to the previous sections.
propagation_line.is_removed = true;
propagate_line_node_remove(propagation_line);
}
}
}
}
// Create lines sections without filtered-out lines.
std::vector<Lines> lines_sections_out(line_nodes_sections.size());
for (const std::vector<LineNode> &line_nodes_section : line_nodes_sections) {
const size_t section_idx = &line_nodes_section - line_nodes_sections.data();
for (const LineNode &line_node : line_nodes_section) {
if (!line_node.is_removed) {
lines_sections_out[section_idx].emplace_back(line_node.line);
}
}
}
return lines_sections_out;
}
void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &normal_infill, ExPolygons &narrow_infill)
{
assert(fill.surface.surface_type == stInternalSolid);
@@ -152,11 +397,6 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
constexpr double connect_extrusions = true;
auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) {
return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) ||
(bhigh >= alow && bhigh <= ahigh);
};
const coord_t scaled_spacing = scaled<coord_t>(fill.params.spacing);
double distance_limit_reconnection = 2.0 * double(scaled_spacing);
double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection;
@@ -180,22 +420,23 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
AABBTreeLines::LinesDistancer<Line> area_walls{to_lines(inner_area)};
const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing;
std::vector<Line> vertical_lines(n_vlines);
coord_t y_min = bb.min.y();
coord_t y_max = bb.max.y();
const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing;
const coord_t y_min = bb.min.y();
const coord_t y_max = bb.max.y();
Lines vertical_lines(n_vlines);
for (size_t i = 0; i < n_vlines; i++) {
coord_t x = bb.min.x() + i * double(scaled_spacing);
vertical_lines[i].a = Point{x, y_min};
vertical_lines[i].b = Point{x, y_max};
}
if (vertical_lines.size() > 0) {
if (!vertical_lines.empty()) {
vertical_lines.push_back(vertical_lines.back());
vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min};
vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max};
}
std::vector<std::vector<Line>> polygon_sections(n_vlines);
std::vector<Lines> polygon_sections(n_vlines);
for (size_t i = 0; i < n_vlines; i++) {
const auto intersections = area_walls.intersections_with_line<true>(vertical_lines[i]);
@@ -211,87 +452,7 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
}
}
struct Node
{
int section_idx;
int line_idx;
int skips_taken = 0;
bool neighbours_explored = false;
std::vector<std::pair<int, int>> neighbours{};
};
coord_t length_filter = scale_(4);
size_t skips_allowed = 2;
size_t min_removal_conut = 5;
for (int section_idx = 0; section_idx < int(polygon_sections.size()); ++section_idx) {
for (int line_idx = 0; line_idx < int(polygon_sections[section_idx].size()); ++line_idx) {
if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) {
std::set<std::pair<int, int>> to_remove{{section_idx, line_idx}};
std::vector<Node> to_visit{{section_idx, line_idx}};
bool initial_touches_long_lines = false;
if (section_idx > 0) {
for (int prev_line_idx = 0; prev_line_idx < int(polygon_sections[section_idx - 1].size()); ++prev_line_idx) {
if (const Line &nl = polygon_sections[section_idx - 1][prev_line_idx];
nl.a != nl.b && segments_overlap(line.a.y(), line.b.y(), nl.a.y(), nl.b.y())) {
initial_touches_long_lines = true;
}
}
}
while (!to_visit.empty()) {
Node curr = to_visit.back();
const Line &curr_l = polygon_sections[curr.section_idx][curr.line_idx];
if (curr.neighbours_explored) {
bool is_valid_for_removal = (curr_l.length() < length_filter) &&
((int(to_remove.size()) - curr.skips_taken > int(min_removal_conut)) ||
(curr.neighbours.empty() && !initial_touches_long_lines));
if (!is_valid_for_removal) {
for (const auto &n : curr.neighbours) {
if (to_remove.find(n) != to_remove.end()) {
is_valid_for_removal = true;
break;
}
}
}
if (!is_valid_for_removal) {
to_remove.erase({curr.section_idx, curr.line_idx});
}
to_visit.pop_back();
} else {
to_visit.back().neighbours_explored = true;
int curr_index = to_visit.size() - 1;
bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < int(skips_allowed);
if (curr.section_idx + 1 < int(polygon_sections.size())) {
for (int lidx = 0; lidx < int(polygon_sections[curr.section_idx + 1].size()); ++lidx) {
if (const Line &nl = polygon_sections[curr.section_idx + 1][lidx];
nl.a != nl.b && segments_overlap(curr_l.a.y(), curr_l.b.y(), nl.a.y(), nl.b.y()) &&
(nl.length() < length_filter || can_use_skip)) {
to_visit[curr_index].neighbours.push_back({curr.section_idx + 1, lidx});
to_remove.insert({curr.section_idx + 1, lidx});
Node next_node{curr.section_idx + 1, lidx, curr.skips_taken + (nl.length() >= length_filter)};
to_visit.push_back(next_node);
}
}
}
}
}
for (const auto &pair : to_remove) {
Line &l = polygon_sections[pair.first][pair.second];
l.a = l.b;
}
}
}
}
for (size_t section_idx = 0; section_idx < polygon_sections.size(); section_idx++) {
polygon_sections[section_idx].erase(std::remove_if(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(),
[](const Line &s) { return s.a == s.b; }),
polygon_sections[section_idx].end());
std::sort(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(),
[](const Line &a, const Line &b) { return a.a.y() < b.b.y(); });
}
polygon_sections = filter_vibrating_extrusions(polygon_sections);
Polygons reconstructed_area{};
// reconstruct polygon from polygon sections
+6 -1
View File
@@ -6813,7 +6813,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return false;
}
std::string type = (volume->type() == ModelVolumeType::MODEL_PART)?"model":"other";
// Orca#7574: always use "model" type to follow the 3MF Core Specification:
// https://github.com/3MFConsortium/spec_core/blob/20c079eef39e45ed223b8443dc9f34cbe32dc2c2/3MF%20Core%20Specification.md#3431-item-element
// > Note: items MUST NOT reference objects of type "other", either directly or recursively.
// This won't break anything because when loading the file Orca (and Bambu) simply does not care about the actual object type at all (as long as it's one of "model" & "other");
// But PrusaSlicer requires the type to be "model".
std::string type = "model";
output_buffer += " <";
output_buffer += OBJECT_TAG;
+44 -34
View File
@@ -1792,6 +1792,7 @@ enum BambuBedType {
bbtEngineeringPlate = 2,
bbtHighTemperaturePlate = 3,
bbtTexturedPEIPlate = 4,
bbtSuperTackPlate = 5,
};
static BambuBedType to_bambu_bed_type(BedType type)
@@ -1807,6 +1808,8 @@ static BambuBedType to_bambu_bed_type(BedType type)
bambu_bed_type = bbtTexturedPEIPlate;
else if (type == btPCT)
bambu_bed_type = bbtCoolPlate;
else if (type == btSuperTack)
bambu_bed_type = bbtSuperTackPlate;
return bambu_bed_type;
}
@@ -1898,36 +1901,42 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
if (!print.config().small_area_infill_flow_compensation_model.empty())
m_small_area_infill_flow_compensator = make_unique<SmallAreaInfillFlowCompensator>(print.config());
file.write_format("; HEADER_BLOCK_START\n");
// Write information on the generator.
file.write_format("; generated by %s on %s\n", Slic3r::header_slic3r_generated().c_str(), Slic3r::Utils::local_timestamp().c_str());
if (is_bbl_printers)
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
//BBS: total layer number
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Total_Layer_Number_Placeholder).c_str());
m_enable_exclude_object = config().exclude_object;
//Orca: extra check for bbl printer
if (is_bbl_printers) {
if (print.calib_params().mode == CalibMode::Calib_None) { // Don't support skipping in cali mode
// list all label_object_id with sorted order here
m_enable_exclude_object = true;
m_label_objects_ids.clear();
m_label_objects_ids.reserve(print.num_object_instances());
for (const PrintObject *print_object : print.objects())
for (const PrintInstance &print_instance : print_object->instances())
m_label_objects_ids.push_back(print_instance.model_instance->get_labeled_id());
std::sort(m_label_objects_ids.begin(), m_label_objects_ids.end());
std::string objects_id_list = "; model label id: ";
for (auto it = m_label_objects_ids.begin(); it != m_label_objects_ids.end(); it++)
objects_id_list += (std::to_string(*it) + (it != m_label_objects_ids.end() - 1 ? "," : "\n"));
file.writeln(objects_id_list);
} else {
m_enable_exclude_object = false;
m_label_objects_ids.clear();
}
// Orca: Don't output Header block if BTT thumbnail is identified in the list
// Get the thumbnails value as a string
std::string thumbnails_value = print.config().option<ConfigOptionString>("thumbnails")->value;
// search string for the BTT_TFT label
bool has_BTT_thumbnail = (thumbnails_value.find("BTT_TFT") != std::string::npos);
if(!has_BTT_thumbnail){
file.write_format("; HEADER_BLOCK_START\n");
// Write information on the generator.
file.write_format("; generated by %s on %s\n", Slic3r::header_slic3r_generated().c_str(), Slic3r::Utils::local_timestamp().c_str());
if (is_bbl_printers)
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
//BBS: total layer number
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Total_Layer_Number_Placeholder).c_str());
m_enable_exclude_object = config().exclude_object;
//Orca: extra check for bbl printer
if (is_bbl_printers) {
if (print.calib_params().mode == CalibMode::Calib_None) { // Don't support skipping in cali mode
// list all label_object_id with sorted order here
m_enable_exclude_object = true;
m_label_objects_ids.clear();
m_label_objects_ids.reserve(print.num_object_instances());
for (const PrintObject *print_object : print.objects())
for (const PrintInstance &print_instance : print_object->instances())
m_label_objects_ids.push_back(print_instance.model_instance->get_labeled_id());
std::sort(m_label_objects_ids.begin(), m_label_objects_ids.end());
std::string objects_id_list = "; model label id: ";
for (auto it = m_label_objects_ids.begin(); it != m_label_objects_ids.end(); it++)
objects_id_list += (std::to_string(*it) + (it != m_label_objects_ids.end() - 1 ? "," : "\n"));
file.writeln(objects_id_list);
} else {
m_enable_exclude_object = false;
m_label_objects_ids.clear();
}
}
{
@@ -1949,7 +1958,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
}
file.write_format("; HEADER_BLOCK_END\n\n");
}
// BBS: write global config at the beginning of gcode file because printer
// need these config information
@@ -5981,7 +5990,7 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string
m_wipe.reset_path();*/
Point last_post_before_retract = this->last_pos();
gcode += this->retract(false, false, lift_type);
gcode += this->retract(false, false, lift_type, role);
// When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters.
// Because of it, it is necessary to call avoid crossing perimeters again with new starting point after calling retraction()
// FIXME Lukas H.: Try to predict if this second calling of avoid crossing perimeters will be needed or not. It could save computations.
@@ -6183,7 +6192,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp
return true;
}
std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType lift_type)
std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType lift_type, ExtrusionRole role)
{
std::string gcode;
@@ -6201,7 +6210,8 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li
(the extruder might be already retracted fully or partially). We call these
methods even if we performed wipe, since this will ensure the entire retraction
length is honored in case wipe path was too short. */
gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract();
if (role != erTopSolidInfill || EXTRUDER_CONFIG(retract_on_top_layer))
gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract();
gcode += m_writer.reset_e();
// Orca: check if should + can lift (roughly from SuperSlicer)
+1 -1
View File
@@ -223,7 +223,7 @@ public:
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment, double z = DBL_MAX);
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift, ExtrusionRole role = erNone);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false);
bool is_BBL_Printer();
+14 -1
View File
@@ -50,7 +50,11 @@ float get_axis_value(const std::string& line, char axis)
char match[3] = " X";
match[1] = axis;
size_t pos = line.find(match) + 2;
size_t pos = line.find(match);
if (pos == std::string::npos) {
return NAN;
}
pos += 2;
//size_t end = std::min(line.find(' ', pos + 1), line.find(';', pos + 1));
// Try to parse the numeric value.
const char* c = line.c_str();
@@ -83,6 +87,15 @@ int16_t get_fan_speed(const std::string &line, GCodeFlavor flavor) {
if (flavor == (gcfMach3) || flavor == (gcfMachinekit)) {
return (int16_t)get_axis_value(line, 'P');
} else {
// Bambu machines use both M106 P1(not P0!) and M106 for part cooling fan.
// Non-bambu machines usually use M106 (without P parameter) for part cooling fan.
// P2 is reserved for auxiliary fan regardless of bambu or not.
// To keep compatibility with Bambu machines, we accept M106 and M106 P1 as the only two valid form
// of gcode that control the part cooling fan. Any other command will be ignored!
const auto idx = get_axis_value(line, 'P');
if (!isnan(idx) && idx != 1.0f) {
return -1;
}
return (int16_t)get_axis_value(line, 'S');
}
} else if (line.compare(0, 4, "M127") == 0 || line.compare(0, 4, "M107") == 0) {
+20
View File
@@ -98,6 +98,8 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags_compatible = {
const std::string GCodeProcessor::Flush_Start_Tag = " FLUSH_START";
const std::string GCodeProcessor::Flush_End_Tag = " FLUSH_END";
//Orca: External device purge tag
const std::string GCodeProcessor::External_Purge_Tag = " EXTERNAL_PURGE";
const float GCodeProcessor::Wipe_Width = 0.05f;
const float GCodeProcessor::Wipe_Height = 0.05f;
@@ -2289,6 +2291,24 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
m_flushing = false;
return;
}
// Orca: Integrate filament consumption for purging performed to an external device and controlled via macros
// (eg. Happy Hare) in the filament consumption stats.
if (boost::starts_with(comment, GCodeProcessor::External_Purge_Tag)) {
std::regex numberRegex(R"(\d+\.\d+)");
std::smatch match;
std::string line(comment);
if (std::regex_search(line, match, numberRegex)) {
float filament_diameter = (static_cast<size_t>(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back();
float filament_radius = 0.5f * filament_diameter;
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
float dE = std::stof(match.str());
float volume_extruded_filament = area_filament_cross_section * dE;
m_used_filaments.update_flush_per_filament(m_extruder_id, volume_extruded_filament);
}
return;
}
if (!producers_enabled || m_producer == EProducer::OrcaSlicer) {
// height tag
+1
View File
@@ -272,6 +272,7 @@ class Print;
static const std::vector<std::string> Reserved_Tags_compatible;
static const std::string Flush_Start_Tag;
static const std::string Flush_End_Tag;
static const std::string External_Purge_Tag;
public:
enum class ETags : unsigned char
{
+6
View File
@@ -447,6 +447,12 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
// this function, fix it first.
//std::terminate();
// Orca: If moving down during below the current layer nominal Z, force XY->Z moves to avoid collisions with previous extrusions
double nominal_z = m_pos(2) - m_lifted;
if (point(2) < nominal_z - EPSILON) { // EPSILON to avoid false matches due to rounding errors
this->set_current_position_clear(false); // This forces XYZ moves to be split into XY->Z
}
/* If target Z is lower than current Z but higher than nominal Z we
don't perform the Z move but we only move in the XY plane and
adjust the nominal Z by reducing the lift amount that will be
+74 -47
View File
@@ -717,53 +717,6 @@ Transformation Transformation::volume_to_bed_transformation(const Transformation
return out;
}
TransformationSVD::TransformationSVD(const Transform3d& trafo)
{
const auto &m0 = trafo.matrix().block<3, 3>(0, 0);
mirror = m0.determinant() < 0.0;
Matrix3d m;
if (mirror)
m = m0 * Eigen::DiagonalMatrix<double, 3, 3>(-1.0, 1.0, 1.0);
else
m = m0;
const Eigen::JacobiSVD<Matrix3d> svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV);
u = svd.matrixU();
v = svd.matrixV();
s = svd.singularValues().asDiagonal();
scale = !s.isApprox(Matrix3d::Identity());
anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2));
rotation = !v.isApprox(u);
if (anisotropic_scale) {
rotation_90_degrees = true;
for (int i = 0; i < 3; ++i) {
const Vec3d row = v.row(i).cwiseAbs();
const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.);
const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.);
if (num_zeros != 2 || num_ones != 1) {
rotation_90_degrees = false;
break;
}
}
// Detect skew by brute force: check if the axes are still orthogonal after transformation
const Matrix3d trafo_linear = trafo.linear();
const std::array<Vec3d, 3> axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() };
std::array<Vec3d, 3> transformed_axes;
for (int i = 0; i < 3; ++i) {
transformed_axes[i] = trafo_linear * axes[i];
}
skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON ||
std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON ||
std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON;
// This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492)
// skew = ! rotation_90_degrees;
} else
skew = false;
}
// For parsing a transformation matrix from 3MF / AMF.
Transform3d transform3d_from_string(const std::string& transform_str)
{
@@ -812,4 +765,78 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
return (axis.z() < 0) ? -angle : angle;
}
TransformationSVD::TransformationSVD(const Transform3d& trafo)
{
const auto &m0 = trafo.matrix().block<3, 3>(0, 0);
mirror = m0.determinant() < 0.0;
Matrix3d m;
if (mirror)
m = m0 * Eigen::DiagonalMatrix<double, 3, 3>(-1.0, 1.0, 1.0);
else
m = m0;
const Eigen::JacobiSVD<Matrix3d> svd(m, Eigen::ComputeFullU | Eigen::ComputeFullV);
u = svd.matrixU();
v = svd.matrixV();
s = svd.singularValues().asDiagonal();
scale = !s.isApprox(Matrix3d::Identity());
anisotropic_scale = ! is_approx(s(0, 0), s(1, 1)) || ! is_approx(s(1, 1), s(2, 2));
rotation = !v.isApprox(u);
if (anisotropic_scale) {
rotation_90_degrees = true;
for (int i = 0; i < 3; ++i) {
const Vec3d row = v.row(i).cwiseAbs();
const size_t num_zeros = is_approx(row[0], 0.) + is_approx(row[1], 0.) + is_approx(row[2], 0.);
const size_t num_ones = is_approx(row[0], 1.) + is_approx(row[1], 1.) + is_approx(row[2], 1.);
if (num_zeros != 2 || num_ones != 1) {
rotation_90_degrees = false;
break;
}
}
// Detect skew by brute force: check if the axes are still orthogonal after transformation
const Matrix3d trafo_linear = trafo.linear();
const std::array<Vec3d, 3> axes = { Vec3d::UnitX(), Vec3d::UnitY(), Vec3d::UnitZ() };
std::array<Vec3d, 3> transformed_axes;
for (int i = 0; i < 3; ++i) {
transformed_axes[i] = trafo_linear * axes[i];
}
skew = std::abs(transformed_axes[0].dot(transformed_axes[1])) > EPSILON ||
std::abs(transformed_axes[1].dot(transformed_axes[2])) > EPSILON ||
std::abs(transformed_axes[2].dot(transformed_axes[0])) > EPSILON;
// This following old code does not work under all conditions. The v matrix can become non diagonal (see SPE-1492)
// skew = ! rotation_90_degrees;
} else
skew = false;
}
Transformation mat_around_a_point_rotate(const Transformation &InMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian)
{
auto xyz = InMat.get_offset();
Transformation left;
left.set_offset(-xyz); // at world origin
auto curMat = left * InMat;
auto qua = Eigen::Quaterniond(Eigen::AngleAxisd(rotate_theta_radian, axis));
qua.normalize();
Transform3d cur_matrix;
Transformation rotateMat4;
rotateMat4.set_matrix(cur_matrix.fromPositionOrientationScale(Vec3d(0., 0., 0.), qua, Vec3d(1., 1., 1.)));
curMat = rotateMat4 * curMat; // along_fix_axis
// rotate mat4 along fix pt
Transformation temp_world;
auto qua_world = Eigen::Quaterniond(Eigen::AngleAxisd(0, axis));
qua_world.normalize();
Transform3d cur_matrix_world;
temp_world.set_matrix(cur_matrix_world.fromPositionOrientationScale(pt, qua_world, Vec3d(1., 1., 1.)));
auto temp_xyz = temp_world.get_matrix().inverse() * xyz;
auto new_pos = temp_world.get_matrix() * (rotateMat4.get_matrix() * temp_xyz);
curMat.set_offset(new_pos);
return curMat;
}
}} // namespace Slic3r::Geometry
+2 -1
View File
@@ -10,7 +10,7 @@
// Serialization through the Cereal library
#include <cereal/access.hpp>
namespace Slic3r {
namespace Slic3r {
namespace ClipperLib {
class PolyNode;
@@ -544,6 +544,7 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
}
Transformation mat_around_a_point_rotate(const Transformation& innMat, const Vec3d &pt, const Vec3d &axis, float rotate_theta_radian);
} } // namespace Slicer::Geometry
#endif
+3 -5
View File
@@ -163,12 +163,10 @@ VoronoiDiagram::detect_known_issues(const VoronoiDiagram &voronoi_diagram, Segme
return IssueType::FINITE_EDGE_WITH_NON_FINITE_VERTEX;
} else if (const IssueType cell_issue_type = detect_known_voronoi_cell_issues(voronoi_diagram, segment_begin, segment_end); cell_issue_type != IssueType::NO_ISSUE_DETECTED) {
return cell_issue_type;
} else if (!VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segment_begin, segment_end)) {
// Detection of non-planar Voronoi diagram detects at least GH issues #8474, #8514 and #8446.
return IssueType::NON_PLANAR_VORONOI_DIAGRAM;
}
// BBS: test no problem in BBS
//} else if (!VoronoiUtilsCgal::is_voronoi_diagram_planar_angle(voronoi_diagram, segment_begin, segment_end)) {
// // Detection of non-planar Voronoi diagram detects at least GH issues #8474, #8514 and #8446.
// return IssueType::NON_PLANAR_VORONOI_DIAGRAM;
//}
return IssueType::NO_ISSUE_DETECTED;
}
+286 -107
View File
@@ -13,7 +13,27 @@
namespace Slic3r {
namespace Measure {
bool get_point_projection_to_plane(const Vec3d &pt, const Vec3d &plane_origin, const Vec3d &plane_normal, Vec3d &intersection_pt)
{
auto normal = plane_normal.normalized();
auto BA = plane_origin - pt;
auto length = BA.dot(normal);
intersection_pt = pt + length * normal;
return true;
}
Vec3d get_one_point_in_plane(const Vec3d &plane_origin, const Vec3d &plane_normal)
{
Vec3d dir(1, 0, 0);
float eps = 1e-3;
if (abs(plane_normal.dot(dir)) > 1 - eps) {
dir = Vec3d(0, 1, 0);
}
auto new_pt = plane_origin + dir;
Vec3d retult;
get_point_projection_to_plane(new_pt, plane_origin, plane_normal, retult);
return retult;
}
constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it
@@ -33,7 +53,7 @@ static std::tuple<Vec3d, double, double> get_center_and_radius(const std::vector
double error = std::numeric_limits<double>::max();
auto circle = Geometry::circle_ransac(out, iter, &error);
return std::make_tuple(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius, error);
}
@@ -69,16 +89,18 @@ public:
bool features_extracted = false;
};
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point);
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran,bool only_select_plane);
int get_num_of_planes() const;
const std::vector<int>& get_plane_triangle_indices(int idx) const;
std::vector<int>* get_plane_tri_indices(int idx);
const std::vector<SurfaceFeature>& get_plane_features(unsigned int plane_id);
std::vector<SurfaceFeature>* get_plane_features_pointer(unsigned int plane_id);
const indexed_triangle_set& get_its() const;
private:
void update_planes();
void extract_features(int plane_idx);
std::vector<PlaneData> m_planes;
std::vector<size_t> m_face_to_plane;
indexed_triangle_set m_its;
@@ -158,7 +180,7 @@ void MeasuringImpl::update_planes()
m_planes.back().normal = normal_ptr->cast<double>();
std::sort(m_planes.back().facets.begin(), m_planes.back().facets.end());
}
// Check that each facet is part of one of the planes.
assert(std::none_of(m_face_to_plane.begin(), m_face_to_plane.end(), [](size_t val) { return val == size_t(-1); }));
@@ -175,7 +197,7 @@ void MeasuringImpl::update_planes()
const auto& facets = planes[plane_id].facets;
planes[plane_id].borders.clear();
std::vector<std::array<bool, 3>> visited(facets.size(), {false, false, false});
for (int face_id=0; face_id<int(facets.size()); ++face_id) {
assert(face_to_plane[facets[face_id]] == plane_id);
@@ -193,7 +215,7 @@ void MeasuringImpl::update_planes()
Halfedge_index he = sm.halfedge(Face_index(facets[face_id]));
while (he.side() != edge_id)
he = sm.next(he);
// he is the first halfedge on the border. Now walk around and append the points.
//const Halfedge_index he_orig = he;
planes[plane_id].borders.emplace_back();
@@ -202,7 +224,7 @@ void MeasuringImpl::update_planes()
last_border.emplace_back(sm.point(sm.source(he)).cast<double>());
//Vertex_index target = sm.target(he);
const Halfedge_index he_start = he;
Face_index fi = he.face();
auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi));
assert(face_it != facets.end());
@@ -228,7 +250,7 @@ void MeasuringImpl::update_planes()
he = sm.opposite(he);
if (he.is_invalid())
goto PLANE_FAILURE;
Face_index fi = he.face();
auto face_it = std::lower_bound(facets.begin(), facets.end(), int(fi));
if (face_it == facets.end() || *face_it != int(fi)) // This indicates a broken mesh.
@@ -265,11 +287,6 @@ void MeasuringImpl::update_planes()
m_planes.shrink_to_fit();
}
void MeasuringImpl::extract_features(int plane_idx)
{
assert(! m_planes[plane_idx].features_extracted);
@@ -490,7 +507,7 @@ void MeasuringImpl::extract_features(int plane_idx)
Vec3d cog = Vec3d::Zero();
size_t counter = 0;
for (const std::vector<Vec3d>& b : plane.borders) {
for (size_t i = 1; i < b.size(); ++i) {
for (size_t i = 0; i < b.size(); ++i) {
cog += b[i];
++counter;
}
@@ -505,14 +522,7 @@ void MeasuringImpl::extract_features(int plane_idx)
plane.features_extracted = true;
}
std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const Vec3d& point)
std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran,bool only_select_plane)
{
if (face_idx >= m_face_to_plane.size())
return std::optional<SurfaceFeature>();
@@ -521,7 +531,7 @@ std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const
if (! plane.features_extracted)
extract_features(m_face_to_plane[face_idx]);
size_t closest_feature_idx = size_t(-1);
double min_dist = std::numeric_limits<double>::max();
@@ -530,40 +540,57 @@ std::optional<SurfaceFeature> MeasuringImpl::get_feature(size_t face_idx, const
assert(plane.surface_features.empty() || plane.surface_features.back().get_type() == SurfaceFeatureType::Plane);
for (size_t i=0; i<plane.surface_features.size() - 1; ++i) {
// The -1 is there to prevent measuring distance to the plane itself,
// which is needless and relatively expensive.
res = get_measurement(plane.surface_features[i], point_sf);
if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented.
double dist = res.distance_strict->dist;
if (dist < feature_hover_limit && dist < min_dist) {
min_dist = std::min(dist, min_dist);
closest_feature_idx = i;
if (!only_select_plane) {
for (size_t i = 0; i < plane.surface_features.size() - 1; ++i) {
// The -1 is there to prevent measuring distance to the plane itself,
// which is needless and relatively expensive.
res = get_measurement(plane.surface_features[i], point_sf);
if (res.distance_strict) { // TODO: this should become an assert after all combinations are implemented.
double dist = res.distance_strict->dist;
if (dist < feature_hover_limit && dist < min_dist) {
min_dist = std::min(dist, min_dist);
closest_feature_idx = i;
}
}
}
}
if (closest_feature_idx != size_t(-1)) {
const SurfaceFeature& f = plane.surface_features[closest_feature_idx];
if (f.get_type() == SurfaceFeatureType::Edge) {
// If this is an edge, check if we are not close to the endpoint. If so,
// we will include the endpoint as well. Close = 10% of the lenghth of
// the edge, clamped between 0.025 and 0.5 mm.
const auto& [sp, ep] = f.get_edge();
double len_sq = (ep-sp).squaredNorm();
double limit_sq = std::max(0.025*0.025, std::min(0.5*0.5, 0.1 * 0.1 * len_sq));
if (closest_feature_idx != size_t(-1)) {
const SurfaceFeature &f = plane.surface_features[closest_feature_idx];
if (f.get_type() == SurfaceFeatureType::Edge) {
// If this is an edge, check if we are not close to the endpoint. If so,
// we will include the endpoint as well. Close = 10% of the lenghth of
// the edge, clamped between 0.025 and 0.5 mm.
const auto &[sp, ep] = f.get_edge();
double len_sq = (ep - sp).squaredNorm();
double limit_sq = std::max(0.025 * 0.025, std::min(0.5 * 0.5, 0.1 * 0.1 * len_sq));
if ((point - sp).squaredNorm() < limit_sq) {
SurfaceFeature local_f(sp);
local_f.origin_surface_feature = std::make_shared<SurfaceFeature>(local_f);
local_f.translate(world_tran);
return std::make_optional(local_f);
}
if ((point-sp).squaredNorm() < limit_sq)
return std::make_optional(SurfaceFeature(sp));
if ((point-ep).squaredNorm() < limit_sq)
return std::make_optional(SurfaceFeature(ep));
if ((point - ep).squaredNorm() < limit_sq) {
SurfaceFeature local_f(ep);
local_f.origin_surface_feature = std::make_shared<SurfaceFeature>(local_f);
local_f.translate(world_tran);
return std::make_optional(local_f);
}
}
SurfaceFeature f_tran(f);
f_tran.origin_surface_feature = std::make_shared<SurfaceFeature>(f);
f_tran.translate(world_tran);
return std::make_optional(f_tran);
}
return std::make_optional(f);
}
// Nothing detected, return the plane as a whole.
assert(plane.surface_features.back().get_type() == SurfaceFeatureType::Plane);
return std::make_optional(plane.surface_features.back());
auto cur_plane = const_cast<PlaneData*>(&plane);
SurfaceFeature f_tran(cur_plane->surface_features.back());
f_tran.origin_surface_feature = std::make_shared<SurfaceFeature>(cur_plane->surface_features.back());
f_tran.translate(world_tran);
return std::make_optional(f_tran);
}
@@ -583,6 +610,12 @@ const std::vector<int>& MeasuringImpl::get_plane_triangle_indices(int idx) const
return m_planes[idx].facets;
}
std::vector<int>* MeasuringImpl::get_plane_tri_indices(int idx)
{
assert(idx >= 0 && idx < int(m_planes.size()));
return &m_planes[idx].facets;
}
const std::vector<SurfaceFeature>& MeasuringImpl::get_plane_features(unsigned int plane_id)
{
assert(plane_id < m_planes.size());
@@ -591,21 +624,18 @@ const std::vector<SurfaceFeature>& MeasuringImpl::get_plane_features(unsigned in
return m_planes[plane_id].surface_features;
}
std::vector<SurfaceFeature>* MeasuringImpl::get_plane_features_pointer(unsigned int plane_id) {
assert(plane_id < m_planes.size());
if (!m_planes[plane_id].features_extracted)
extract_features(plane_id);
return &m_planes[plane_id].surface_features;
}
const indexed_triangle_set& MeasuringImpl::get_its() const
{
return this->m_its;
}
Measuring::Measuring(const indexed_triangle_set& its)
: priv{std::make_unique<MeasuringImpl>(its)}
{}
@@ -614,9 +644,12 @@ Measuring::~Measuring() {}
std::optional<SurfaceFeature> Measuring::get_feature(size_t face_idx, const Vec3d& point) const
std::optional<SurfaceFeature> Measuring::get_feature(size_t face_idx, const Vec3d &point, const Transform3d &world_tran, bool only_select_plane) const
{
return priv->get_feature(face_idx, point);
if (face_idx == 7516 || face_idx == 7517) {
std::cout << "";
}
return priv->get_feature(face_idx, point, world_tran, only_select_plane);
}
@@ -796,13 +829,7 @@ static AngleAndEdges angle_plane_plane(const std::tuple<int, Vec3d, Vec3d>& p1,
return ret;
}
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring)
MeasurementResult get_measurement(const SurfaceFeature &a, const SurfaceFeature &b, bool deal_circle_result)
{
assert(a.get_type() != SurfaceFeatureType::Undef && b.get_type() != SurfaceFeatureType::Undef);
@@ -819,7 +846,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
if (f2.get_type() == SurfaceFeatureType::Point) {
Vec3d diff = (f2.get_point() - f1.get_point());
result.distance_strict = std::make_optional(DistAndPoints{diff.norm(), f1.get_point(), f2.get_point()});
result.distance_xyz = diff.cwiseAbs();
result.distance_xyz = diff;
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Edge) {
@@ -849,13 +876,18 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
result.distance_strict = std::make_optional(DistAndPoints{ radius, c, p_on_circle });
}
else {
const Eigen::Hyperplane<double, 3> circle_plane(n, c);
const Vec3d proj = circle_plane.projection(f1.get_point());
const double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) +
(f1.get_point() - proj).squaredNorm());
if (deal_circle_result == false) {
const Eigen::Hyperplane<double, 3> circle_plane(n, c);
const Vec3d proj = circle_plane.projection(f1.get_point());
const double dist = std::sqrt(std::pow((proj - c).norm() - radius, 2.) + (f1.get_point() - proj).squaredNorm());
const Vec3d p_on_circle = c + radius * (proj - c).normalized();
result.distance_strict = std::make_optional(DistAndPoints{ dist, f1.get_point(), p_on_circle }); // TODO
const Vec3d p_on_circle = c + radius * (proj - c).normalized();
result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), p_on_circle});
}
else {
const double dist = (f1.get_point() - c).norm();
result.distance_strict = std::make_optional(DistAndPoints{dist, f1.get_point(), c});
}
}
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
@@ -903,31 +935,31 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
result.angle = angle_edge_edge(f1.get_edge(), f2.get_edge());
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Circle) {
const std::pair<Vec3d, Vec3d> e = f1.get_edge();
const auto& [center, radius, normal] = f2.get_circle();
const Vec3d e1e2 = (e.second - e.first);
const Vec3d e1e2_unit = e1e2.normalized();
const std::pair<Vec3d, Vec3d> e = f1.get_edge();
const auto &[center, radius, normal] = f2.get_circle();
const Vec3d e1e2 = (e.second - e.first);
const Vec3d e1e2_unit = e1e2.normalized();
std::vector<DistAndPoints> distances;
distances.emplace_back(*get_measurement(SurfaceFeature(e.first), f2).distance_strict);
distances.emplace_back(*get_measurement(SurfaceFeature(e.second), f2).distance_strict);
const Eigen::Hyperplane<double, 3> plane(e1e2_unit, center);
const Eigen::ParametrizedLine<double, 3> line = Eigen::ParametrizedLine<double, 3>::Through(e.first, e.second);
const Vec3d inter = line.intersectionPoint(plane);
const Vec3d e1inter = inter - e.first;
if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm())
distances.emplace_back(*get_measurement(SurfaceFeature(inter), f2).distance_strict);
const Eigen::Hyperplane<double, 3> plane(e1e2_unit, center);
const Eigen::ParametrizedLine<double, 3> line = Eigen::ParametrizedLine<double, 3>::Through(e.first, e.second);
const Vec3d inter = line.intersectionPoint(plane);
const Vec3d e1inter = inter - e.first;
if (e1inter.dot(e1e2) >= 0.0 && e1inter.norm() < e1e2.norm()) distances.emplace_back(*get_measurement(SurfaceFeature(inter), f2).distance_strict);
auto it = std::min_element(distances.begin(), distances.end(),
[](const DistAndPoints& item1, const DistAndPoints& item2) {
return item1.dist < item2.dist;
});
result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to});
auto it = std::min_element(distances.begin(), distances.end(), [](const DistAndPoints &item1, const DistAndPoints &item2) { return item1.dist < item2.dist; });
if (deal_circle_result == false) {
result.distance_infinite = std::make_optional(DistAndPoints{it->dist, it->from, it->to});
}
else{
const double dist = (it->from - center).norm();
result.distance_infinite = std::make_optional(DistAndPoints{dist, it->from, center});
}
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
assert(measuring != nullptr);
const auto [from, to] = f1.get_edge();
const auto [idx, normal, origin] = f2.get_plane();
@@ -944,9 +976,9 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to });
}
else {
const std::vector<SurfaceFeature>& plane_features = measuring->get_plane_features(idx);
auto plane_features = f2.world_plane_features;
std::vector<DistAndPoints> distances;
for (const SurfaceFeature& sf : plane_features) {
for (const SurfaceFeature& sf : *plane_features) {
if (sf.get_type() == SurfaceFeatureType::Edge) {
const auto m = get_measurement(sf, f1);
if (!m.distance_infinite.has_value()) {
@@ -975,7 +1007,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
const auto [c0, r0, n0] = f1.get_circle();
const auto [c1, r1, n1] = f2.get_circle();
// The following code is an adaptation of the algorithm found in:
// The following code is an adaptation of the algorithm found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/DistCircle3Circle3.h
// and described in:
// https://www.geometrictools.com/Documentation/DistanceToCircle3.pdf
@@ -1120,7 +1152,7 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
}
else {
ClosestInfo& info = candidates[0];
const double N0dD = n0.dot(D);
const Vec3d normProj = N0dD * n0;
const Vec3d compProj = D - normProj;
@@ -1185,22 +1217,26 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
}
}
info.sqrDistance = distance * distance + N0dD * N0dD;
info.sqrDistance = distance * distance;
}
if (deal_circle_result == false) {
result.distance_infinite = std::make_optional(
DistAndPoints{std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest}); // TODO
} else {
const double dist = (c0 - c1).norm();
result.distance_strict = std::make_optional(DistAndPoints{dist, c0, c1});
}
result.distance_infinite = std::make_optional(DistAndPoints{ std::sqrt(candidates[0].sqrDistance), candidates[0].circle0Closest, candidates[0].circle1Closest }); // TODO
///////////////////////////////////////////////////////////////////////////
} else if (f2.get_type() == SurfaceFeatureType::Plane) {
assert(measuring != nullptr);
const auto [center, radius, normal1] = f1.get_circle();
const auto [idx2, normal2, origin2] = f2.get_plane();
const bool coplanar = are_parallel(normal1, normal2) && Eigen::Hyperplane<double, 3>(normal1, center).absDistance(origin2) < EPSILON;
if (!coplanar) {
const std::vector<SurfaceFeature>& plane_features = measuring->get_plane_features(idx2);
auto plane_features = f2.world_plane_features;
std::vector<DistAndPoints> distances;
for (const SurfaceFeature& sf : plane_features) {
for (const SurfaceFeature& sf : *plane_features) {
if (sf.get_type() == SurfaceFeatureType::Edge) {
const auto m = get_measurement(sf, f1);
if (!m.distance_infinite.has_value()) {
@@ -1218,6 +1254,13 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
});
result.distance_infinite = std::make_optional(DistAndPoints{ it->dist, it->from, it->to });
}
else {
const Eigen::Hyperplane<double, 3> plane(normal2, origin2);
result.distance_infinite = std::make_optional(DistAndPoints{plane.absDistance(center), center, plane.projection(center)});
}
}
else {
result.distance_strict = std::make_optional(DistAndPoints{0, center, origin2});
}
}
///////////////////////////////////////////////////////////////////////////
@@ -1229,23 +1272,159 @@ MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature&
if (are_parallel(normal1, normal2)) {
// The planes are parallel, calculate distance.
const Eigen::Hyperplane<double, 3> plane(normal1, pt1);
result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt2), pt2, plane.projection(pt2) }); // TODO
const Eigen::Hyperplane<double, 3> plane(normal2, pt2);
result.distance_infinite = std::make_optional(DistAndPoints{ plane.absDistance(pt1), pt1, plane.projection(pt1) });
}
else
result.angle = angle_plane_plane(f1.get_plane(), f2.get_plane());
}
if (swap) {
auto swap_dist_and_points = [](DistAndPoints& dp) {
auto back = dp.to;
dp.to = dp.from;
dp.from = back;
};
if (result.distance_infinite.has_value()) {
swap_dist_and_points(*result.distance_infinite);
}
if (result.distance_strict.has_value()) {
swap_dist_and_points(*result.distance_strict);
}
}
return result;
}
bool can_set_xyz_distance(const SurfaceFeature &a, const SurfaceFeature &b) {
const bool swap = int(a.get_type()) > int(b.get_type());
const SurfaceFeature &f1 = swap ? b : a;
const SurfaceFeature &f2 = swap ? a : b;
if (f1.get_type() == SurfaceFeatureType::Point){
if (f2.get_type() == SurfaceFeatureType::Point) {
return true;
}
}
else if (f1.get_type() == SurfaceFeatureType::Circle) {
if (f2.get_type() == SurfaceFeatureType::Circle) {
return true;
}
}
return false;
}
AssemblyAction get_assembly_action(const SurfaceFeature& a, const SurfaceFeature& b)
{
AssemblyAction action;
const SurfaceFeature &f1 = a;
const SurfaceFeature &f2 = b;
if (f1.get_type() == SurfaceFeatureType::Plane) {
action.can_set_feature_1_reverse_rotation = true;
if (f2.get_type() == SurfaceFeatureType::Plane) {
const auto [idx1, normal1, pt1] = f1.get_plane();
const auto [idx2, normal2, pt2] = f2.get_plane();
action.can_set_to_center_coincidence = true;
action.can_set_feature_2_reverse_rotation = true;
if (are_parallel(normal1, normal2)) {
action.can_set_to_parallel = false;
action.has_parallel_distance = true;
action.can_around_center_of_faces = true;
Vec3d proj_pt2;
Measure::get_point_projection_to_plane(pt2, pt1, normal1, proj_pt2);
action.parallel_distance = (pt2 - proj_pt2).norm();
if ((pt2 - proj_pt2).dot(normal1) < 0) {
action.parallel_distance = -action.parallel_distance;
}
action.angle_radian = 0;
} else {
action.can_set_to_parallel = true;
action.has_parallel_distance = false;
action.can_around_center_of_faces = false;
action.parallel_distance = 0;
action.angle_radian = std::acos(std::clamp(normal2.dot(-normal1), -1.0, 1.0));
}
}
}
return action;
}
void SurfaceFeature::translate(const Vec3d& displacement) {
switch (get_type()) {
case Measure::SurfaceFeatureType::Point: {
m_pt1 = m_pt1 + displacement;
break;
}
case Measure::SurfaceFeatureType::Edge: {
m_pt1 = m_pt1 + displacement;
m_pt2 = m_pt2 + displacement;
if (m_pt3.has_value()) { //extra_point()
m_pt3 = *m_pt3 + displacement;
}
break;
}
case Measure::SurfaceFeatureType::Plane: {
//m_pt1 is normal;
m_pt2 = m_pt2 + displacement;
break;
}
case Measure::SurfaceFeatureType::Circle: {
m_pt1 = m_pt1 + displacement;
// m_pt2 is normal;
break;
}
default: break;
}
}
void SurfaceFeature::translate(const Transform3d &tran)
{
switch (get_type()) {
case Measure::SurfaceFeatureType::Point: {
m_pt1 = tran * m_pt1;
break;
}
case Measure::SurfaceFeatureType::Edge: {
m_pt1 = tran * m_pt1;
m_pt2 = tran * m_pt2;
if (m_pt3.has_value()) { // extra_point()
m_pt3 = tran * *m_pt3;
}
break;
}
case Measure::SurfaceFeatureType::Plane: {
// m_pt1 is normal;
Vec3d temp_pt1 = m_pt2 + m_pt1;
temp_pt1 = tran * temp_pt1;
m_pt2 = tran * m_pt2;
m_pt1 = (temp_pt1 - m_pt2).normalized();
break;
}
case Measure::SurfaceFeatureType::Circle: {
// m_pt1 is center;
// m_pt2 is normal;
auto local_normal = m_pt2;
auto local_center = m_pt1;
Vec3d temp_pt2 = local_normal + local_center;
temp_pt2 = tran * temp_pt2;
m_pt1 = tran * m_pt1;
auto world_center = m_pt1;
m_pt2 = (temp_pt2 - m_pt1).normalized();
} // namespace Measure
auto calc_world_radius = [&local_center, &local_normal, &tran, &world_center](const Vec3d &pt, double &value) {
Vec3d intersection_pt;
get_point_projection_to_plane(pt, local_center, local_normal, intersection_pt);
auto local_radius_pt = (intersection_pt - local_center).normalized() * value + local_center;
auto radius_pt = tran * local_radius_pt;
value = (radius_pt - world_center).norm();
};
//m_value is radius
auto new_pt = get_one_point_in_plane(local_center, local_normal);
calc_world_radius(new_pt, m_value);
break;
}
default: break;
}
}
}//namespace Measure
} // namespace Slic3r
+57 -12
View File
@@ -6,18 +6,13 @@
#include "Point.hpp"
struct indexed_triangle_set;
namespace Slic3r {
class TriangleMesh;
namespace Measure {
enum class SurfaceFeatureType : int {
Undef = 0,
Point = 1 << 0,
@@ -26,22 +21,44 @@ enum class SurfaceFeatureType : int {
Plane = 1 << 3
};
class SurfaceFeature {
bool get_point_projection_to_plane(const Vec3d &pt, const Vec3d &plane_origin, const Vec3d &plane_normal, Vec3d &intersection_pt);
Vec3d get_one_point_in_plane(const Vec3d &plane_origin, const Vec3d &plane_normal);
class SurfaceFeature
{
public:
SurfaceFeature(SurfaceFeatureType type, const Vec3d& pt1, const Vec3d& pt2, std::optional<Vec3d> pt3 = std::nullopt, double value = 0.0)
: m_type(type), m_pt1(pt1), m_pt2(pt2), m_pt3(pt3), m_value(value) {}
explicit SurfaceFeature(const Vec3d& pt)
SurfaceFeature(const Vec3d& pt)
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
SurfaceFeature(const SurfaceFeature& sf){
this->clone(sf);
volume = sf.volume;
plane_indices = sf.plane_indices;
world_tran = sf.world_tran;
world_plane_features = sf.world_plane_features;
origin_surface_feature = sf.origin_surface_feature;
}
void clone(const SurfaceFeature &sf)
{
m_type = sf.get_type();
m_pt1 = sf.get_pt1();
m_pt2 = sf.get_pt2();
m_pt3 = sf.get_pt3();
m_value = sf.get_value();
}
void translate(const Vec3d& displacement);
void translate(const Transform3d& tran);
// Get type of this feature.
SurfaceFeatureType get_type() const { return m_type; }
// For points, return the point.
Vec3d get_point() const { assert(m_type == SurfaceFeatureType::Point); return m_pt1; }
// For edges, return start and end.
std::pair<Vec3d, Vec3d> get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); }
std::pair<Vec3d, Vec3d> get_edge() const { assert(m_type == SurfaceFeatureType::Edge); return std::make_pair(m_pt1, m_pt2); }
// For circles, return center, radius and normal.
std::tuple<Vec3d, double, Vec3d> get_circle() const { assert(m_type == SurfaceFeatureType::Circle); return std::make_tuple(m_pt1, m_value, m_pt2); }
@@ -75,6 +92,17 @@ public:
return !operator == (other);
}
void* volume{nullptr};
std::vector<int>* plane_indices{nullptr};
Transform3d world_tran;
std::shared_ptr<std::vector<SurfaceFeature>> world_plane_features{nullptr};
std::shared_ptr<SurfaceFeature> origin_surface_feature{nullptr};
Vec3d get_pt1() const{ return m_pt1; }
Vec3d get_pt2() const { return m_pt2; }
const std::optional<Vec3d>& get_pt3() const { return m_pt3; }
double get_value() const { return m_value; }
private:
SurfaceFeatureType m_type{ SurfaceFeatureType::Undef };
Vec3d m_pt1{ Vec3d::Zero() };
@@ -97,7 +125,7 @@ public:
// Given a face_idx where the mouse cursor points, return a feature that
// should be highlighted (if any).
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point) const;
std::optional<SurfaceFeature> get_feature(size_t face_idx, const Vec3d& point, const Transform3d & world_tran,bool only_select_plane) const;
// Return total number of planes.
int get_num_of_planes() const;
@@ -111,7 +139,7 @@ public:
// Returns the mesh used for measuring
const indexed_triangle_set& get_its() const;
private:
private:
std::unique_ptr<MeasuringImpl> priv;
};
@@ -152,7 +180,24 @@ struct MeasurementResult {
};
// Returns distance/angle between two SurfaceFeatures.
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b, const Measuring* measuring = nullptr);
MeasurementResult get_measurement(const SurfaceFeature& a, const SurfaceFeature& b,bool deal_circle_result =false);
bool can_set_xyz_distance(const SurfaceFeature &a, const SurfaceFeature &b);
struct AssemblyAction
{
bool can_set_to_parallel{false};
bool can_set_to_center_coincidence{false};
bool can_set_feature_1_reverse_rotation{false};
bool can_set_feature_2_reverse_rotation{false};
bool can_around_center_of_faces{false};
bool has_parallel_distance{false};
float parallel_distance;
float angle_radian{0};
Transform3d tran_for_parallel;
Transform3d tran_for_center_coincidence;
Transform3d tran_for_reverse_rotation;
};
AssemblyAction get_assembly_action(const SurfaceFeature &a, const SurfaceFeature &b);
inline Vec3d edge_direction(const Vec3d& from, const Vec3d& to) { return (to - from).normalized(); }
inline Vec3d edge_direction(const std::pair<Vec3d, Vec3d>& e) { return edge_direction(e.first, e.second); }
+4 -4
View File
@@ -7,7 +7,7 @@ namespace Slic3r {
namespace Measure {
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Polynomial1.h
class Polynomial1
@@ -174,7 +174,7 @@ inline Polynomial1 operator * (double scalar, const Polynomial1& p)
}
// Utility class used to calculate distance circle-circle
// Adaptation of code found in:
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/RootsPolynomial.h
class RootsPolynomial
@@ -242,7 +242,7 @@ public:
return false;
if (tmin >= tmax)
// Invalid ordering of interval endpoitns.
// Invalid ordering of interval endpoitns.
return false;
for (uint32_t i = 1; i <= maxIterations; ++i) {
@@ -345,7 +345,7 @@ public:
}
};
// Adaptation of code found in:
// Adaptation of code found in:
// https://github.com/davideberly/GeometricTools/blob/master/GTE/Mathematics/Vector.h
// Construct a single vector orthogonal to the nonzero input vector. If
-1
View File
@@ -2785,7 +2785,6 @@ std::vector<size_t> ModelVolume::get_extruders_from_multi_material_painting() co
if (!this->is_mm_painted())
return {};
assert(static_cast<size_t>(TriangleStateType::Extruder1) - 1 == 0);
const TriangleSelector::TriangleSplittingData &data = this->mmu_segmentation_facets.get_data();
std::vector<size_t> extruders;
+12 -1
View File
@@ -1222,6 +1222,17 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& p
smooth_overhang_level(paths);
}
if (overhangs_reverse) {
for (const ExtrusionPath& path : paths) {
if (path.role() == erOverhangPerimeter) {
if (pg_extrusion.is_contour)
steep_overhang_contour = true;
else
steep_overhang_hole = true;
break;
}
}
}
}
}
else {
@@ -3168,7 +3179,7 @@ void PerimeterGenerator::process_arachne()
// printf("Layer ID %d, Outer index %d, inner index %d, second inner index %d, maximum internal perimeter %d \n",layer_id,outer,first_internal,second_internal, max_internal);
if (outer > -1 && first_internal > -1 && second_internal > -1) { // found all three perimeters to re-order? If not the perimeters will be processed outside in.
std::vector<PerimeterGeneratorArachneExtrusion> inner_outer_extrusions; // temporary array to hold extrusions for reordering
inner_outer_extrusions.reserve(max_internal - position + 1); // reserve array containing the number of perimeters before a new island. Variables are array indexes hence need to add +1 to convert to position allocations
inner_outer_extrusions.resize(max_internal - position + 1); // reserve array containing the number of perimeters before a new island. Variables are array indexes hence need to add +1 to convert to position allocations
// printf("Allocated array size %d, max_internal index %d, start position index %d \n",max_internal-position+1,max_internal,position);
for (arr_j = max_internal; arr_j >=position; --arr_j){ // go inside out towards the external perimeter (perimeters in reverse order) and store all internal perimeters until the first one identified with inset index 2
+1 -1
View File
@@ -823,7 +823,7 @@ static std::vector<std::string> s_Preset_filament_options {
"filament_flow_ratio", "filament_density", "filament_cost", "filament_minimal_purge_on_wipe_tower",
"nozzle_temperature", "nozzle_temperature_initial_layer",
// BBS
"cool_plate_temp", "textured_cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "textured_cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer","textured_plate_temp_initial_layer",
"cool_plate_temp", "textured_cool_plate_temp", "eng_plate_temp", "hot_plate_temp", "textured_plate_temp", "cool_plate_temp_initial_layer", "textured_cool_plate_temp_initial_layer", "eng_plate_temp_initial_layer", "hot_plate_temp_initial_layer", "textured_plate_temp_initial_layer", "supertack_plate_temp_initial_layer", "supertack_plate_temp",
// "bed_type",
//BBS:temperature_vitrification
"temperature_vitrification", "reduce_fan_stop_start_freq","dont_slow_down_outer_wall", "slow_down_for_layer_cooling", "fan_min_speed",
+20
View File
@@ -1851,6 +1851,26 @@ void PresetBundle::export_selections(AppConfig &config)
}
// BBS
void PresetBundle::set_num_filaments(unsigned int n, std::vector<std::string> new_colors) {
int old_filament_count = this->filament_presets.size();
if (n > old_filament_count && old_filament_count != 0)
filament_presets.resize(n, filament_presets.back());
else {
filament_presets.resize(n);
}
ConfigOptionStrings* filament_color = project_config.option<ConfigOptionStrings>("filament_colour");
filament_color->resize(n);
ams_multi_color_filment.resize(n);
// BBS set new filament color to new_color
if (old_filament_count < n) {
if (!new_colors.empty()) {
for (int i = old_filament_count; i < n; i++) {
filament_color->values[i] = new_colors[i - old_filament_count];
}
}
}
update_multi_material_filament_presets();
}
void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
{
int old_filament_count = this->filament_presets.size();
+1
View File
@@ -111,6 +111,7 @@ public:
void export_selections(AppConfig &config);
// BBS
void set_num_filaments(unsigned int n, std::vector<std::string> new_colors);
void set_num_filaments(unsigned int n, std::string new_col = "");
unsigned int sync_ams_list(unsigned int & unknowns);
//BBS: check whether this is the only edited filament
+4
View File
@@ -129,6 +129,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"sparse_infill_acceleration",
"internal_solid_infill_acceleration",
// BBS
"supertack_plate_temp_initial_layer",
"cool_plate_temp_initial_layer",
"textured_cool_plate_temp_initial_layer",
"eng_plate_temp_initial_layer",
@@ -148,6 +149,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"retraction_minimum_travel",
"retract_before_wipe",
"retract_when_changing_layer",
"retract_on_top_layer",
"retraction_length",
"retract_length_toolchange",
"z_hop",
@@ -269,6 +271,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "gcode_flavor"
|| opt_key == "single_extruder_multi_material"
|| opt_key == "nozzle_temperature"
// BBS
|| opt_key == "supertack_plate_temp"
|| opt_key == "cool_plate_temp"
|| opt_key == "textured_cool_plate_temp"
|| opt_key == "eng_plate_temp"
+1 -1
View File
@@ -1506,7 +1506,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
num_extruders > 1 &&
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
std::array<bool, static_cast<size_t>(EnforcerBlockerType::ExtruderMax)> used_facet_states{};
std::array<bool, static_cast<size_t>(EnforcerBlockerType::ExtruderMax) + 1> used_facet_states{};
for (const ModelVolume *volume : volumes) {
const std::vector<bool> &volume_used_facet_states = volume->mmu_segmentation_facets.get_data().used_states;
+37 -6
View File
@@ -350,6 +350,7 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(OverhangFanThreshold)
// BBS
static const t_config_enum_values s_keys_map_BedType = {
{ "Default Plate", btDefault },
{ "Supertack Plate", btSuperTack },
{ "Cool Plate", btPC },
{ "Engineering Plate", btEP },
{ "High Temp Plate", btPEI },
@@ -657,6 +658,16 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloatOrPercent(0., false));
// BBS
def = this->add("supertack_plate_temp", coInts);
def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers except the initial one. "
"Value 0 means the filament does not support to print on the Cool Plate");
def->sidetext = "°C";
def->full_label = L("Bed temperature");
def->min = 0;
def->max = 120;
def->set_default_value(new ConfigOptionInts{35});
def = this->add("cool_plate_temp", coInts);
def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers except the initial one. "
@@ -707,6 +718,16 @@ void PrintConfigDef::init_fff_params()
def->max = 300;
def->set_default_value(new ConfigOptionInts{45});
def = this->add("supertack_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
def->tooltip = L("Bed temperature of the initial layer. "
"Value 0 means the filament does not support to print on the Bambu Cool Plate SuperTack");
def->sidetext = "°C";
def->min = 0;
def->max = 120;
def->set_default_value(new ConfigOptionInts{ 35 });
def = this->add("cool_plate_temp_initial_layer", coInts);
def->label = L("Initial layer");
def->full_label = L("Initial layer bed temperature");
@@ -762,12 +783,14 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->enum_keys_map = &s_keys_map_BedType;
// Orca: make sure the order of the values is the same as the BedType enum
def->enum_values.emplace_back("Supertack Plate");
def->enum_values.emplace_back("Cool Plate");
def->enum_values.emplace_back("Engineering Plate");
def->enum_values.emplace_back("High Temp Plate");
def->enum_values.emplace_back("Textured PEI Plate");
def->enum_values.emplace_back("Textured Cool Plate");
def->enum_labels.emplace_back(L("Smooth Cool Plate"));
def->enum_labels.emplace_back(L("Bambu Cool Plate SuperTack"));
def->enum_labels.emplace_back(L("Smooth Cool Plate / PLA Plate"));
def->enum_labels.emplace_back(L("Engineering Plate"));
def->enum_labels.emplace_back(L("Smooth High Temp Plate"));
def->enum_labels.emplace_back(L("Textured PEI Plate"));
@@ -3711,6 +3734,12 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBools { false });
def = this->add("retract_on_top_layer", coBools);
def->label = L("Retract on top layer");
def->tooltip = L("Force a retraction on top layer. Disabling could prevent clog on very slow patterns with small movements, like Hilbert curve");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBools { true });
def = this->add("retraction_length", coFloats);
def->label = L("Length");
def->full_label = L("Retraction Length");
@@ -3751,7 +3780,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloats { 10. });
def = this->add("z_hop", coFloats);
def->label = L("Z hop when retract");
def->label = L("Z-hop height");
def->tooltip = L("Whenever the retraction is done, the nozzle is lifted a little to create clearance between nozzle and the print. "
"It prevents nozzle from hitting the print when travel move. "
"Using spiral line to lift z can prevent stringing");
@@ -3779,7 +3808,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("z_hop_types", coEnums);
def->label = L("Z hop type");
def->label = L("Z-hop type");
def->tooltip = L("Z hop type");
def->enum_keys_map = &ConfigOptionEnum<ZHopType>::get_enum_values();
def->enum_values.push_back("Auto Lift");
@@ -5371,7 +5400,7 @@ void PrintConfigDef::init_fff_params()
// BBS: floats
"wipe_distance",
// bools
"retract_when_changing_layer", "wipe",
"retract_when_changing_layer", "retract_on_top_layer", "wipe",
// percents
"retract_before_wipe",
"long_retractions_when_cut",
@@ -5423,7 +5452,7 @@ void PrintConfigDef::init_extruder_option_keys()
"nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset",
"retraction_length", "z_hop", "z_hop_types", "travel_slope", "retract_lift_above", "retract_lift_below", "retract_lift_enforce", "retraction_speed", "deretraction_speed",
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour",
"retract_when_changing_layer", "retract_on_top_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour",
"default_filament_profile","retraction_distances_when_cut","long_retractions_when_cut"
};
@@ -5434,6 +5463,7 @@ void PrintConfigDef::init_extruder_option_keys()
"retract_lift_above",
"retract_lift_below",
"retract_lift_enforce",
"retract_on_top_layer",
"retract_restart_extra",
"retract_when_changing_layer",
"retraction_distances_when_cut",
@@ -5455,7 +5485,7 @@ void PrintConfigDef::init_filament_option_keys()
"filament_diameter", "min_layer_height", "max_layer_height",
"retraction_length", "z_hop", "z_hop_types", "retract_lift_above", "retract_lift_below", "retract_lift_enforce", "retraction_speed", "deretraction_speed",
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "filament_colour",
"retract_when_changing_layer", "retract_on_top_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "filament_colour",
"default_filament_profile","retraction_distances_when_cut","long_retractions_when_cut"/*,"filament_seam_gap"*/
};
@@ -5468,6 +5498,7 @@ void PrintConfigDef::init_filament_option_keys()
"retract_lift_enforce",
"retract_restart_extra",
"retract_when_changing_layer",
"retract_on_top_layer",
"retraction_distances_when_cut",
"retraction_length",
"retraction_minimum_travel",
+13
View File
@@ -254,6 +254,7 @@ enum OverhangFanThreshold {
// BBS
enum BedType {
btDefault = 0,
btSuperTack,
btPC,
btEP,
btPEI,
@@ -322,6 +323,9 @@ static std::string bed_type_to_gcode_string(const BedType type)
std::string type_str;
switch (type) {
case btSuperTack:
type_str = "supertack_plate";
break;
case btPC:
type_str = "cool_plate";
break;
@@ -347,6 +351,9 @@ static std::string bed_type_to_gcode_string(const BedType type)
static std::string get_bed_temp_key(const BedType type)
{
if (type == btSuperTack)
return "supertack_plate_temp";
if (type == btPC)
return "cool_plate_temp";
@@ -367,6 +374,9 @@ static std::string get_bed_temp_key(const BedType type)
static std::string get_bed_temp_1st_layer_key(const BedType type)
{
if (type == btSuperTack)
return "supertack_plate_temp_initial_layer";
if (type == btPC)
return "cool_plate_temp_initial_layer";
@@ -1183,9 +1193,11 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionEnum<BedType>, curr_bed_type))
((ConfigOptionInts, cool_plate_temp))
((ConfigOptionInts, textured_cool_plate_temp))
((ConfigOptionInts, supertack_plate_temp))
((ConfigOptionInts, eng_plate_temp))
((ConfigOptionInts, hot_plate_temp)) // hot is short for high temperature
((ConfigOptionInts, textured_plate_temp))
((ConfigOptionInts, supertack_plate_temp_initial_layer))
((ConfigOptionInts, cool_plate_temp_initial_layer))
((ConfigOptionInts, textured_cool_plate_temp_initial_layer))
((ConfigOptionInts, eng_plate_temp_initial_layer))
@@ -1239,6 +1251,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloat, resolution))
((ConfigOptionFloats, retraction_minimum_travel))
((ConfigOptionBools, retract_when_changing_layer))
((ConfigOptionBools, retract_on_top_layer))
((ConfigOptionFloat, skirt_distance))
((ConfigOptionInt, skirt_height))
((ConfigOptionInt, skirt_loops))
+5 -1
View File
@@ -31,6 +31,7 @@
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
#include <tbb/spin_mutex.h>
#include <Shiny/Shiny.h>
@@ -2512,7 +2513,10 @@ void PrintObject::bridge_over_infill()
[](const Line &s) { return s.a == s.b; }),
polygon_sections[i].end());
std::sort(polygon_sections[i].begin(), polygon_sections[i].end(),
[](const Line &a, const Line &b) { return a.a.y() < b.b.y(); });
[](const Line &a, const Line &b) {
if (a == b) return false; // Ensure irreflexivity
return a.a.y() < b.b.y();
});
}
// reconstruct polygon from polygon sections
+21 -5
View File
@@ -1259,14 +1259,15 @@ void PrintObject::apply_conical_overhang() {
auto upper_poly = upper_layer->merged(float(SCALED_EPSILON));
upper_poly = union_ex(upper_poly);
// Merge layer for the same reason
auto current_poly = layer->merged(float(SCALED_EPSILON));
current_poly = union_ex(current_poly);
// Avoid closing up of recessed holes in the base of a model.
// Detects when a hole is completely covered by the layer above and removes the hole from the layer above before
// adding it in.
// This should have no effect any time a hole in a layer interacts with any polygon in the layer above
if (scaled_max_hole_area > 0.0) {
// Merge layer for the same reason
auto current_poly = layer->merged(float(SCALED_EPSILON));
current_poly = union_ex(current_poly);
// Now go through all the holes in the current layer and check if they intersect anything in the layer above
// If not, then they're the top of a hole and should be cut from the layer above before the union
@@ -1301,10 +1302,25 @@ void PrintObject::apply_conical_overhang() {
}
// Calculate the scaled upper poly that belongs to current region
auto p = intersection_ex(upper_layer->m_regions[region_id]->slices.surfaces, upper_poly);
// And now union it
auto p = union_ex(intersection_ex(upper_layer->m_regions[region_id]->slices.surfaces, upper_poly));
// Remove all islands that have already been fully covered by current layer
p.erase(std::remove_if(p.begin(), p.end(), [&current_poly](const ExPolygon& ex) {
return diff_ex(ex, current_poly).empty();
}), p.end());
// And now union it with current region
ExPolygons layer_polygons = to_expolygons(layer->m_regions[region_id]->slices.surfaces);
layer->m_regions[region_id]->slices.set(union_ex(layer_polygons, p), stInternal);
// Then remove it from all other regions, to avoid overlapping regions
for (size_t other_region = 0; other_region < this->num_printing_regions(); ++other_region) {
if (other_region == region_id) {
continue;
}
ExPolygons s = to_expolygons(layer->m_regions[other_region]->slices.surfaces);
layer->m_regions[other_region]->slices.set(diff_ex(s, p, ApplySafetyOffset::Yes), stInternal);
}
}
//layer->export_region_slices_to_svg_debug("layer_after_conical_overhang");
}
+3 -2
View File
@@ -1910,14 +1910,15 @@ static inline void improve_ordering_by_two_exchanges_with_segment_flipping(Polyl
for (const FlipEdge &edge : edges) {
Polyline &pl = polylines[edge.source_index];
out.emplace_back(std::move(pl));
if (edge.p2 == pl.first_point().cast<double>()) {
if (edge.p2 == out.back().first_point().cast<double>()) {
// Polyline is flipped.
out.back().reverse();
} else {
// Polyline is not flipped.
assert(edge.p1 == pl.first_point().cast<double>());
assert(edge.p1 == out.back().first_point().cast<double>());
}
}
polylines = out;
#ifndef NDEBUG
double cost_final = cost();
+13
View File
@@ -319,6 +319,13 @@ std::vector<double> layer_height_profile_adaptive(const SlicingParameters& slici
else if (layer_height_profile.back() > height && layer_height_profile.back() - height > LAYER_HEIGHT_CHANGE_STEP)
height = layer_height_profile.back() - LAYER_HEIGHT_CHANGE_STEP;
for (auto const& [range,options] : object.layer_config_ranges) {
if ( print_z >= range.first && print_z <= range.second) {
height = options.opt_float("layer_height");
break;
};
};
layer_height_profile.push_back(print_z);
layer_height_profile.push_back(height);
print_z += height;
@@ -444,6 +451,7 @@ std::vector<double> smooth_height_profile(const std::vector<double>& profile, co
}
void adjust_layer_height_profile(
const ModelObject &model_object,
const SlicingParameters &slicing_params,
std::vector<coordf_t> &layer_height_profile,
coordf_t z,
@@ -478,6 +486,11 @@ void adjust_layer_height_profile(
}
}
for (auto const& [range,options] : model_object.layer_config_ranges) {
if (z >= range.first - current_layer_height && z <= range.second + current_layer_height)
return;
};
// 2) Is it possible to apply the delta?
switch (action) {
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:
+1
View File
@@ -176,6 +176,7 @@ enum LayerHeightEditActionType : unsigned int {
};
void adjust_layer_height_profile(
const ModelObject &model_object,
const SlicingParameters &slicing_params,
std::vector<coordf_t> &layer_height_profile,
coordf_t z,
+1 -1
View File
@@ -1665,7 +1665,7 @@ TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const {
} else {
// In case this is leaf, we better save information about its state.
int n = int(tr.get_state());
if (n < static_cast<size_t>(EnforcerBlockerType::ExtruderMax))
if (n <= static_cast<size_t>(EnforcerBlockerType::ExtruderMax))
data.used_states[n] = true;
if (n >= 3) {
+2 -2
View File
@@ -256,7 +256,7 @@ public:
// Bit stream containing splitting information.
std::vector<bool> bitstream;
// Array indicating which triangle state types are used (encoded inside bitstream).
std::vector<bool> used_states { std::vector<bool>(static_cast<size_t>(EnforcerBlockerType::ExtruderMax), false) };
std::vector<bool> used_states { std::vector<bool>(static_cast<size_t>(EnforcerBlockerType::ExtruderMax) + 1, false) };
TriangleSplittingData() = default;
@@ -270,7 +270,7 @@ public:
// Reset all used states before they are recomputed based on the bitstream.
void reset_used_states() {
used_states.resize(static_cast<size_t>(EnforcerBlockerType::ExtruderMax), false);
used_states.resize(static_cast<size_t>(EnforcerBlockerType::ExtruderMax) + 1, false);
std::fill(used_states.begin(), used_states.end(), false);
}
+53 -11
View File
@@ -46,13 +46,20 @@ double CalibPressureAdvance::e_per_mm(
return line_flow.mm3_per_mm() * print_flow_ratio / filament_area ;
}
std::string CalibPressureAdvance::convert_number_to_string(double num) const
std::string CalibPressureAdvance::convert_number_to_string(double num, unsigned int precision) const
{
auto sNumber = std::to_string(num);
sNumber.erase(sNumber.find_last_not_of('0') + 1, std::string::npos);
sNumber.erase(sNumber.find_last_not_of('.') + 1, std::string::npos);
std::ostringstream stream;
return sNumber;
if (precision) {
/* if number is > 1000 then there are no way we'll fit fractional part into 5 glyphs, so
* in this case we keep full precision.
* Otherwise we reduce precision by 1 to accomodate decimal separator */
stream << std::setprecision(num >= 1000 ? precision : precision - 1);
}
stream << num;
return stream.str();
}
std::string CalibPressureAdvance::draw_digit(
@@ -201,12 +208,12 @@ std::string CalibPressureAdvance::draw_number(double
double speed,
GCodeWriter &writer)
{
auto sNumber = convert_number_to_string(value);
auto sNumber = convert_number_to_string(value, m_number_len);
std::stringstream gcode;
gcode << writer.set_speed(speed);
for (std::string::size_type i = 0; i < sNumber.length(); ++i) {
if (i > m_max_number_len) {
if (i >= m_number_len) {
break;
}
switch (mode) {
@@ -537,6 +544,18 @@ CalibPressureAdvancePattern::CalibPressureAdvancePattern(
this->m_draw_digit_mode = DrawDigitMode::Bottom_To_Top;
refresh_setup(config, is_bbl_machine, model, origin);
}
double CalibPressureAdvancePattern::flow_val() const
{
double flow_mult = m_config.option<ConfigOptionFloats>("filament_flow_ratio")->get_at(0);
double nozzle_diameter = m_config.option<ConfigOptionFloats>("nozzle_diameter")->get_at(0);
double line_width = m_config.get_abs_value("line_width", nozzle_diameter);
double layer_height = m_config.get_abs_value("layer_height");
double speed = m_config.opt_float("outer_wall_speed");
Flow pattern_line = Flow(line_width, layer_height, m_config.option<ConfigOptionFloats>("nozzle_diameter")->get_at(0));
return speed * pattern_line.mm3_per_mm() * flow_mult;
};
void CalibPressureAdvancePattern::generate_custom_gcodes(const DynamicPrintConfig &config,
@@ -564,7 +583,7 @@ void CalibPressureAdvancePattern::generate_custom_gcodes(const DynamicPrintConfi
draw_box_opt_args.is_filled = true;
draw_box_opt_args.num_perimeters = wall_count();
gcode << draw_box(m_writer, m_starting_point.x(), m_starting_point.y() + frame_size_y() + line_spacing_first_layer(),
glyph_tab_max_x() - m_starting_point.x(),
print_size_x(),
max_numbering_height() + line_spacing_first_layer() + m_glyph_padding_vertical * 2, draw_box_opt_args);
std::vector<CustomGCode::Item> gcode_items;
@@ -593,6 +612,8 @@ void CalibPressureAdvancePattern::generate_custom_gcodes(const DynamicPrintConfi
// line numbering
if (i == 1) {
m_number_len = max_numbering_length();
gcode << m_writer.set_pressure_advance(m_params.start);
double number_e_per_mm = e_per_mm(line_width(), height_layer(),
@@ -606,6 +627,18 @@ void CalibPressureAdvancePattern::generate_custom_gcodes(const DynamicPrintConfi
m_params.start + (j * m_params.step), m_draw_digit_mode, line_width(), number_e_per_mm,
speed_first_layer(), m_writer);
}
// flow value
int line_num = num_patterns + 2;
gcode << draw_number(glyph_start_x(line_num), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
flow_val(), m_draw_digit_mode, line_width(), number_e_per_mm,
speed_first_layer(), m_writer);
// acceleration
line_num = num_patterns + 4;
gcode << draw_number(glyph_start_x(line_num), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
m_config.opt_float("default_acceleration"), m_draw_digit_mode, line_width(), number_e_per_mm,
speed_first_layer(), m_writer);
}
@@ -790,7 +823,7 @@ double CalibPressureAdvancePattern::glyph_tab_max_x() const
(glyph_length_x() - line_width() / 2) + padding;
}
double CalibPressureAdvancePattern::max_numbering_height() const
size_t CalibPressureAdvancePattern::max_numbering_length() const
{
std::string::size_type most_characters = 0;
const int num_patterns = get_num_patterns();
@@ -804,9 +837,18 @@ double CalibPressureAdvancePattern::max_numbering_height() const
}
}
most_characters = std::min(most_characters, m_max_number_len);
std::string sAccel = convert_number_to_string(m_config.opt_float("default_acceleration"));
most_characters = std::max(most_characters, sAccel.length());
return (most_characters * m_digit_segment_len) + ((most_characters - 1) * m_digit_gap_len);
/* don't actually check flow value: we'll print as many fractional digits as fits */
return std::min(most_characters, m_max_number_len);
}
double CalibPressureAdvancePattern::max_numbering_height() const
{
std::string::size_type num_characters = max_numbering_length();
return (num_characters * m_digit_segment_len) + ((num_characters - 1) * m_digit_gap_len);
}
double CalibPressureAdvancePattern::pattern_shift() const
+16 -3
View File
@@ -155,7 +155,7 @@ protected:
double e_per_mm(double line_width, double layer_height, float nozzle_diameter, float filament_diameter, float print_flow_ratio) const;
double speed_adjust(int speed) const { return speed * 60; };
std::string convert_number_to_string(double num) const;
std::string convert_number_to_string(double num, unsigned precision = 0) const;
double number_spacing() const { return m_digit_segment_len + m_digit_gap_len; };
std::string draw_digit(double startx,
double starty,
@@ -188,6 +188,7 @@ protected:
const double m_digit_segment_len{2};
const double m_digit_gap_len{1};
const std::string::size_type m_max_number_len{5};
std::string::size_type m_number_len{m_max_number_len}; /* Current length of number labels */
};
class CalibPressureAdvanceLine : public CalibPressureAdvance
@@ -255,6 +256,7 @@ public:
double print_size_x() const { return object_size_x() + pattern_shift(); };
double print_size_y() const { return object_size_y(); };
double max_layer_z() const { return height_first_layer() + ((m_num_layers - 1) * height_layer()); };
double flow_val() const;
void generate_custom_gcodes(const DynamicPrintConfig &config, bool is_bbl_machine, Model &model, const Vec3d &origin);
@@ -264,8 +266,18 @@ public:
protected:
double speed_first_layer() const { return m_config.option<ConfigOptionFloat>("initial_layer_speed")->value; };
double speed_perimeter() const { return m_config.option<ConfigOptionFloat>("outer_wall_speed")->value; };
double line_width_first_layer() const { return m_config.get_abs_value("initial_layer_line_width"); };
double line_width() const { return m_config.get_abs_value("line_width"); };
double line_width_first_layer() const
{
// TODO: FIXME: find out current filament/extruder?
const double nozzle_diameter = m_config.opt_float("nozzle_diameter", 0);
return m_config.get_abs_value("initial_layer_line_width", nozzle_diameter);
};
double line_width() const
{
// TODO: FIXME: find out current filament/extruder?
const double nozzle_diameter = m_config.opt_float("nozzle_diameter", 0);
return m_config.get_abs_value("line_width", nozzle_diameter);
};
int wall_count() const { return m_config.option<ConfigOptionInt>("wall_loops")->value; };
private:
@@ -296,6 +308,7 @@ private:
double glyph_length_x() const;
double glyph_tab_max_x() const;
double max_numbering_height() const;
size_t max_numbering_length() const;
double pattern_shift() const;
+2
View File
@@ -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
+11 -5
View File
@@ -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);
+4 -2
View File
@@ -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,
+29 -18
View File
@@ -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) {
+2 -2
View File
@@ -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)
}
}
}
}
+1 -1
View File
@@ -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();
+3 -3
View File
@@ -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());
+1
View File
@@ -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; }
+79
View File
@@ -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
+9 -1
View File
@@ -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_
+2 -2
View File
@@ -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();
}
}
+3 -4
View File
@@ -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
{
+167
View File
@@ -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
+43
View File
@@ -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_
+33
View File
@@ -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)
+11 -8
View File
@@ -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(); }
+2 -36
View File
@@ -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();
-1
View File
@@ -335,7 +335,6 @@ private:
void set_center(const Vec3d&center, 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
+177 -45
View File
@@ -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
+29 -25
View File
@@ -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
+20 -1
View File
@@ -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);
+6
View File
@@ -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;
+2 -1
View File
@@ -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);
+3 -2
View File
@@ -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* */
};
+5 -1
View File
@@ -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);
+4 -2
View File
@@ -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;
+14 -15
View File
@@ -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();
+3
View File
@@ -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)
{}
+60
View File
@@ -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)
{
+4
View File
@@ -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
View File
@@ -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();
}
});
+11 -9
View File
@@ -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);
+2
View File
@@ -312,6 +312,8 @@ public:
{
}
};
public:
int getUpdateItemCount() { return m_presetitems.size(); }
private:
std::vector<PresetItem> m_presetitems;
+1 -1
View File
@@ -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) {