mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2026-02-20 19:42:21 -07:00
Merge branch 'main' into feature/OrcaFilamentLibrary
This commit is contained in:
commit
1df52936f2
75 changed files with 6670 additions and 6405 deletions
|
|
@ -1,280 +0,0 @@
|
|||
#include <stack>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "PerimeterOrder.hpp"
|
||||
#include "libslic3r/Arachne/utils/ExtrusionJunction.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
|
||||
namespace Slic3r::Arachne::PerimeterOrder {
|
||||
|
||||
using namespace Arachne;
|
||||
|
||||
static size_t get_extrusion_lines_count(const Perimeters &perimeters) {
|
||||
size_t extrusion_lines_count = 0;
|
||||
for (const Perimeter &perimeter : perimeters)
|
||||
extrusion_lines_count += perimeter.size();
|
||||
|
||||
return extrusion_lines_count;
|
||||
}
|
||||
|
||||
static PerimeterExtrusions get_sorted_perimeter_extrusions_by_area(const Perimeters &perimeters) {
|
||||
PerimeterExtrusions sorted_perimeter_extrusions;
|
||||
sorted_perimeter_extrusions.reserve(get_extrusion_lines_count(perimeters));
|
||||
|
||||
for (const Perimeter &perimeter : perimeters) {
|
||||
for (const ExtrusionLine &extrusion_line : perimeter) {
|
||||
if (extrusion_line.empty())
|
||||
continue; // This shouldn't ever happen.
|
||||
|
||||
const BoundingBox bbox = get_extents(extrusion_line);
|
||||
// Be aware that Arachne produces contours with clockwise orientation and holes with counterclockwise orientation.
|
||||
const double area = std::abs(extrusion_line.area());
|
||||
const Polygon polygon = extrusion_line.is_closed ? to_polygon(extrusion_line) : Polygon{};
|
||||
|
||||
sorted_perimeter_extrusions.emplace_back(extrusion_line, area, polygon, bbox);
|
||||
}
|
||||
}
|
||||
|
||||
// Open extrusions have an area equal to zero, so sorting based on the area ensures that open extrusions will always be before closed ones.
|
||||
std::sort(sorted_perimeter_extrusions.begin(), sorted_perimeter_extrusions.end(),
|
||||
[](const PerimeterExtrusion &l, const PerimeterExtrusion &r) { return l.area < r.area; });
|
||||
|
||||
return sorted_perimeter_extrusions;
|
||||
}
|
||||
|
||||
// Functions fill adjacent_perimeter_extrusions field for every PerimeterExtrusion by pointers to PerimeterExtrusions that contain or are inside this PerimeterExtrusion.
|
||||
static void construct_perimeter_extrusions_adjacency_graph(PerimeterExtrusions &sorted_perimeter_extrusions) {
|
||||
// Construct a graph (defined using adjacent_perimeter_extrusions field) where two PerimeterExtrusion are adjacent when one is inside the other.
|
||||
std::vector<bool> root_candidates(sorted_perimeter_extrusions.size(), false);
|
||||
for (PerimeterExtrusion &perimeter_extrusion : sorted_perimeter_extrusions) {
|
||||
const size_t perimeter_extrusion_idx = &perimeter_extrusion - sorted_perimeter_extrusions.data();
|
||||
|
||||
if (!perimeter_extrusion.is_closed()) {
|
||||
root_candidates[perimeter_extrusion_idx] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (PerimeterExtrusion &root_candidate : sorted_perimeter_extrusions) {
|
||||
const size_t root_candidate_idx = &root_candidate - sorted_perimeter_extrusions.data();
|
||||
|
||||
if (!root_candidates[root_candidate_idx])
|
||||
continue;
|
||||
|
||||
if (perimeter_extrusion.bbox.contains(root_candidate.bbox) && perimeter_extrusion.polygon.contains(root_candidate.extrusion.junctions.front().p)) {
|
||||
perimeter_extrusion.adjacent_perimeter_extrusions.emplace_back(&root_candidate);
|
||||
root_candidate.adjacent_perimeter_extrusions.emplace_back(&perimeter_extrusion);
|
||||
root_candidates[root_candidate_idx] = false;
|
||||
}
|
||||
}
|
||||
|
||||
root_candidates[perimeter_extrusion_idx] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the depth-first search to assign the nearest external perimeter for every PerimeterExtrusion.
|
||||
// When some PerimeterExtrusion is achievable from more than one external perimeter, then we choose the
|
||||
// one that comes from a contour.
|
||||
static void assign_nearest_external_perimeter(PerimeterExtrusions &sorted_perimeter_extrusions) {
|
||||
std::stack<PerimeterExtrusion *> stack;
|
||||
for (PerimeterExtrusion &perimeter_extrusion : sorted_perimeter_extrusions) {
|
||||
if (perimeter_extrusion.is_external_perimeter()) {
|
||||
perimeter_extrusion.depth = 0;
|
||||
perimeter_extrusion.nearest_external_perimeter = &perimeter_extrusion;
|
||||
stack.push(&perimeter_extrusion);
|
||||
}
|
||||
}
|
||||
|
||||
while (!stack.empty()) {
|
||||
PerimeterExtrusion *current_extrusion = stack.top();
|
||||
stack.pop();
|
||||
|
||||
for (PerimeterExtrusion *adjacent_extrusion : current_extrusion->adjacent_perimeter_extrusions) {
|
||||
const size_t adjacent_extrusion_depth = current_extrusion->depth + 1;
|
||||
// Update depth when the new depth is smaller or when we can achieve the same depth from a contour.
|
||||
// This will ensure that the internal perimeter will be extruded before the outer external perimeter
|
||||
// when there are two external perimeters and one internal.
|
||||
if (adjacent_extrusion_depth < adjacent_extrusion->depth) {
|
||||
adjacent_extrusion->nearest_external_perimeter = current_extrusion->nearest_external_perimeter;
|
||||
adjacent_extrusion->depth = adjacent_extrusion_depth;
|
||||
stack.push(adjacent_extrusion);
|
||||
} else if (adjacent_extrusion_depth == adjacent_extrusion->depth && !adjacent_extrusion->nearest_external_perimeter->is_contour() && current_extrusion->is_contour()) {
|
||||
adjacent_extrusion->nearest_external_perimeter = current_extrusion->nearest_external_perimeter;
|
||||
stack.push(adjacent_extrusion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline Point get_end_position(const ExtrusionLine &extrusion) {
|
||||
if (extrusion.is_closed)
|
||||
return extrusion.junctions[0].p; // We ended where we started.
|
||||
else
|
||||
return extrusion.junctions.back().p; // Pick the other end from where we started.
|
||||
}
|
||||
|
||||
// Returns ordered extrusions.
|
||||
static std::vector<const PerimeterExtrusion *> ordered_perimeter_extrusions_to_minimize_distances(Point current_position, std::vector<const PerimeterExtrusion *> extrusions) {
|
||||
// Ensure that open extrusions will be placed before the closed one.
|
||||
std::sort(extrusions.begin(), extrusions.end(),
|
||||
[](const PerimeterExtrusion *l, const PerimeterExtrusion *r) -> bool { return l->is_closed() < r->is_closed(); });
|
||||
|
||||
std::vector<const PerimeterExtrusion *> ordered_extrusions;
|
||||
std::vector<bool> already_selected(extrusions.size(), false);
|
||||
while (ordered_extrusions.size() < extrusions.size()) {
|
||||
double nearest_distance_sqr = std::numeric_limits<double>::max();
|
||||
size_t nearest_extrusion_idx = 0;
|
||||
bool is_nearest_closed = false;
|
||||
|
||||
for (size_t extrusion_idx = 0; extrusion_idx < extrusions.size(); ++extrusion_idx) {
|
||||
if (already_selected[extrusion_idx])
|
||||
continue;
|
||||
|
||||
const ExtrusionLine &extrusion_line = extrusions[extrusion_idx]->extrusion;
|
||||
const Point &extrusion_start_position = extrusion_line.junctions.front().p;
|
||||
const double distance_sqr = (current_position - extrusion_start_position).cast<double>().squaredNorm();
|
||||
if (distance_sqr < nearest_distance_sqr) {
|
||||
if (extrusion_line.is_closed || (!extrusion_line.is_closed && nearest_distance_sqr == std::numeric_limits<double>::max()) || (!extrusion_line.is_closed && !is_nearest_closed)) {
|
||||
nearest_extrusion_idx = extrusion_idx;
|
||||
nearest_distance_sqr = distance_sqr;
|
||||
is_nearest_closed = extrusion_line.is_closed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_selected[nearest_extrusion_idx] = true;
|
||||
const PerimeterExtrusion *nearest_extrusion = extrusions[nearest_extrusion_idx];
|
||||
current_position = get_end_position(nearest_extrusion->extrusion);
|
||||
ordered_extrusions.emplace_back(nearest_extrusion);
|
||||
}
|
||||
|
||||
return ordered_extrusions;
|
||||
}
|
||||
|
||||
struct GroupedPerimeterExtrusions
|
||||
{
|
||||
GroupedPerimeterExtrusions() = delete;
|
||||
explicit GroupedPerimeterExtrusions(const PerimeterExtrusion *external_perimeter_extrusion)
|
||||
: external_perimeter_extrusion(external_perimeter_extrusion) {}
|
||||
|
||||
std::vector<const PerimeterExtrusion *> extrusions;
|
||||
const PerimeterExtrusion *external_perimeter_extrusion = nullptr;
|
||||
};
|
||||
|
||||
// Returns vector of indexes that represent the order of grouped extrusions in grouped_extrusions.
|
||||
static std::vector<size_t> order_of_grouped_perimeter_extrusions_to_minimize_distances(Point current_position, std::vector<GroupedPerimeterExtrusions> grouped_extrusions) {
|
||||
// Ensure that holes will be placed before contour and open extrusions before the closed one.
|
||||
std::sort(grouped_extrusions.begin(), grouped_extrusions.end(), [](const GroupedPerimeterExtrusions &l, const GroupedPerimeterExtrusions &r) -> bool {
|
||||
return (l.external_perimeter_extrusion->is_contour() < r.external_perimeter_extrusion->is_contour()) ||
|
||||
(l.external_perimeter_extrusion->is_contour() == r.external_perimeter_extrusion->is_contour() && l.external_perimeter_extrusion->is_closed() < r.external_perimeter_extrusion->is_closed());
|
||||
});
|
||||
|
||||
const size_t holes_cnt = std::count_if(grouped_extrusions.begin(), grouped_extrusions.end(), [](const GroupedPerimeterExtrusions &grouped_extrusions) {
|
||||
return !grouped_extrusions.external_perimeter_extrusion->is_contour();
|
||||
});
|
||||
|
||||
std::vector<size_t> grouped_extrusions_order;
|
||||
std::vector<bool> already_selected(grouped_extrusions.size(), false);
|
||||
while (grouped_extrusions_order.size() < grouped_extrusions.size()) {
|
||||
double nearest_distance_sqr = std::numeric_limits<double>::max();
|
||||
size_t nearest_grouped_extrusions_idx = 0;
|
||||
bool is_nearest_closed = false;
|
||||
|
||||
// First we order all holes and then we start ordering contours.
|
||||
const size_t grouped_extrusion_end = grouped_extrusions_order.size() < holes_cnt ? holes_cnt: grouped_extrusions.size();
|
||||
for (size_t grouped_extrusion_idx = 0; grouped_extrusion_idx < grouped_extrusion_end; ++grouped_extrusion_idx) {
|
||||
if (already_selected[grouped_extrusion_idx])
|
||||
continue;
|
||||
|
||||
const ExtrusionLine &external_perimeter_extrusion_line = grouped_extrusions[grouped_extrusion_idx].external_perimeter_extrusion->extrusion;
|
||||
const Point &extrusion_start_position = external_perimeter_extrusion_line.junctions.front().p;
|
||||
const double distance_sqr = (current_position - extrusion_start_position).cast<double>().squaredNorm();
|
||||
if (distance_sqr < nearest_distance_sqr) {
|
||||
if (external_perimeter_extrusion_line.is_closed || (!external_perimeter_extrusion_line.is_closed && nearest_distance_sqr == std::numeric_limits<double>::max()) || (!external_perimeter_extrusion_line.is_closed && !is_nearest_closed)) {
|
||||
nearest_grouped_extrusions_idx = grouped_extrusion_idx;
|
||||
nearest_distance_sqr = distance_sqr;
|
||||
is_nearest_closed = external_perimeter_extrusion_line.is_closed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grouped_extrusions_order.emplace_back(nearest_grouped_extrusions_idx);
|
||||
already_selected[nearest_grouped_extrusions_idx] = true;
|
||||
const GroupedPerimeterExtrusions &nearest_grouped_extrusions = grouped_extrusions[nearest_grouped_extrusions_idx];
|
||||
const ExtrusionLine &last_extrusion_line = nearest_grouped_extrusions.extrusions.back()->extrusion;
|
||||
current_position = get_end_position(last_extrusion_line);
|
||||
}
|
||||
|
||||
return grouped_extrusions_order;
|
||||
}
|
||||
|
||||
static PerimeterExtrusions extract_ordered_perimeter_extrusions(const PerimeterExtrusions &sorted_perimeter_extrusions, const bool external_perimeters_first) {
|
||||
// Extrusions are ordered inside each group.
|
||||
std::vector<GroupedPerimeterExtrusions> grouped_extrusions;
|
||||
|
||||
std::stack<const PerimeterExtrusion *> stack;
|
||||
std::vector<bool> visited(sorted_perimeter_extrusions.size(), false);
|
||||
for (const PerimeterExtrusion &perimeter_extrusion : sorted_perimeter_extrusions) {
|
||||
if (!perimeter_extrusion.is_external_perimeter())
|
||||
continue;
|
||||
|
||||
stack.push(&perimeter_extrusion);
|
||||
visited.assign(sorted_perimeter_extrusions.size(), false);
|
||||
|
||||
grouped_extrusions.emplace_back(&perimeter_extrusion);
|
||||
while (!stack.empty()) {
|
||||
const PerimeterExtrusion *current_extrusion = stack.top();
|
||||
const size_t current_extrusion_idx = current_extrusion - sorted_perimeter_extrusions.data();
|
||||
|
||||
stack.pop();
|
||||
visited[current_extrusion_idx] = true;
|
||||
|
||||
if (current_extrusion->nearest_external_perimeter == &perimeter_extrusion) {
|
||||
grouped_extrusions.back().extrusions.emplace_back(current_extrusion);
|
||||
}
|
||||
|
||||
std::vector<const PerimeterExtrusion *> available_candidates;
|
||||
for (const PerimeterExtrusion *adjacent_extrusion : current_extrusion->adjacent_perimeter_extrusions) {
|
||||
const size_t adjacent_extrusion_idx = adjacent_extrusion - sorted_perimeter_extrusions.data();
|
||||
if (!visited[adjacent_extrusion_idx] && !adjacent_extrusion->is_external_perimeter() && adjacent_extrusion->nearest_external_perimeter == &perimeter_extrusion) {
|
||||
available_candidates.emplace_back(adjacent_extrusion);
|
||||
}
|
||||
}
|
||||
|
||||
if (available_candidates.size() == 1) {
|
||||
stack.push(available_candidates.front());
|
||||
} else if (available_candidates.size() > 1) {
|
||||
// When there is more than one available candidate, then order candidates to minimize distances between
|
||||
// candidates and also to minimize the distance from the current_position.
|
||||
std::vector<const PerimeterExtrusion *> adjacent_extrusions = ordered_perimeter_extrusions_to_minimize_distances(Point::Zero(), available_candidates);
|
||||
for (auto extrusion_it = adjacent_extrusions.rbegin(); extrusion_it != adjacent_extrusions.rend(); ++extrusion_it) {
|
||||
stack.push(*extrusion_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!external_perimeters_first)
|
||||
std::reverse(grouped_extrusions.back().extrusions.begin(), grouped_extrusions.back().extrusions.end());
|
||||
}
|
||||
|
||||
const std::vector<size_t> grouped_extrusion_order = order_of_grouped_perimeter_extrusions_to_minimize_distances(Point::Zero(), grouped_extrusions);
|
||||
|
||||
PerimeterExtrusions ordered_extrusions;
|
||||
for (size_t order_idx : grouped_extrusion_order) {
|
||||
for (const PerimeterExtrusion *perimeter_extrusion : grouped_extrusions[order_idx].extrusions)
|
||||
ordered_extrusions.emplace_back(*perimeter_extrusion);
|
||||
}
|
||||
|
||||
return ordered_extrusions;
|
||||
}
|
||||
|
||||
// FIXME: From the point of better patch planning, it should be better to do ordering when we have generated all extrusions (for now, when G-Code is exported).
|
||||
// FIXME: It would be better to extract the adjacency graph of extrusions from the SkeletalTrapezoidation graph.
|
||||
PerimeterExtrusions ordered_perimeter_extrusions(const Perimeters &perimeters, const bool external_perimeters_first) {
|
||||
PerimeterExtrusions sorted_perimeter_extrusions = get_sorted_perimeter_extrusions_by_area(perimeters);
|
||||
construct_perimeter_extrusions_adjacency_graph(sorted_perimeter_extrusions);
|
||||
assign_nearest_external_perimeter(sorted_perimeter_extrusions);
|
||||
return extract_ordered_perimeter_extrusions(sorted_perimeter_extrusions, external_perimeters_first);
|
||||
}
|
||||
|
||||
} // namespace Slic3r::Arachne::PerimeterOrder
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
#ifndef slic3r_GCode_PerimeterOrder_hpp_
|
||||
#define slic3r_GCode_PerimeterOrder_hpp_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
|
||||
#include "libslic3r/Arachne/utils/ExtrusionLine.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
|
||||
namespace Slic3r::Arachne::PerimeterOrder {
|
||||
|
||||
// Data structure stores ExtrusionLine (closed and open) together with additional data.
|
||||
struct PerimeterExtrusion
|
||||
{
|
||||
explicit PerimeterExtrusion(const Arachne::ExtrusionLine &extrusion, const double area, const Polygon &polygon, const BoundingBox &bbox)
|
||||
: extrusion(extrusion), area(area), polygon(polygon), bbox(bbox) {}
|
||||
|
||||
Arachne::ExtrusionLine extrusion;
|
||||
// Absolute value of the area of the polygon. The value is always non-negative, even for holes.
|
||||
double area = 0;
|
||||
|
||||
// Polygon is non-empty only for closed extrusions.
|
||||
Polygon polygon;
|
||||
BoundingBox bbox;
|
||||
|
||||
std::vector<PerimeterExtrusion *> adjacent_perimeter_extrusions;
|
||||
|
||||
// How far is this perimeter from the nearest external perimeter. Contour is always preferred over holes.
|
||||
size_t depth = std::numeric_limits<size_t>::max();
|
||||
PerimeterExtrusion *nearest_external_perimeter = nullptr;
|
||||
|
||||
// Returns if ExtrusionLine is a contour or a hole.
|
||||
bool is_contour() const { return extrusion.is_contour(); }
|
||||
|
||||
// Returns if ExtrusionLine is closed or opened.
|
||||
bool is_closed() const { return extrusion.is_closed; }
|
||||
|
||||
// Returns if ExtrusionLine is an external or an internal perimeter.
|
||||
bool is_external_perimeter() const { return extrusion.is_external_perimeter(); }
|
||||
};
|
||||
|
||||
using PerimeterExtrusions = std::vector<PerimeterExtrusion>;
|
||||
|
||||
PerimeterExtrusions ordered_perimeter_extrusions(const Perimeters &perimeters, bool external_perimeters_first);
|
||||
|
||||
} // namespace Slic3r::Arachne::PerimeterOrder
|
||||
|
||||
#endif // slic3r_GCode_Travels_hpp_
|
||||
|
|
@ -782,4 +782,98 @@ bool WallToolPaths::removeEmptyToolPaths(std::vector<VariableWidthLines> &toolpa
|
|||
return toolpaths.empty();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the order constraints of the insets when printing walls per region / hole.
|
||||
* Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right.
|
||||
*
|
||||
* Odd walls should always go after their enclosing wall polygons.
|
||||
*
|
||||
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
|
||||
*/
|
||||
WallToolPaths::ExtrusionLineSet WallToolPaths::getRegionOrder(const std::vector<ExtrusionLine *> &input, const bool outer_to_inner)
|
||||
{
|
||||
ExtrusionLineSet order_requirements;
|
||||
// We build a grid where we map toolpath vertex locations to toolpaths,
|
||||
// so that we can easily find which two toolpaths are next to each other,
|
||||
// which is the requirement for there to be an order constraint.
|
||||
//
|
||||
// We use a PointGrid rather than a LineGrid to save on computation time.
|
||||
// In very rare cases two insets might lie next to each other without having neighboring vertices, e.g.
|
||||
// \ .
|
||||
// | / .
|
||||
// | / .
|
||||
// || .
|
||||
// | \ .
|
||||
// | \ .
|
||||
// / .
|
||||
// However, because of how Arachne works this will likely never be the case for two consecutive insets.
|
||||
// On the other hand one could imagine that two consecutive insets of a very large circle
|
||||
// could be simplify()ed such that the remaining vertices of the two insets don't align.
|
||||
// In those cases the order requirement is not captured,
|
||||
// which means that the PathOrderOptimizer *might* result in a violation of the user set path order.
|
||||
// This problem is expected to be not so severe and happen very sparsely.
|
||||
|
||||
coord_t max_line_w = 0u;
|
||||
for (const ExtrusionLine *line : input) // compute max_line_w
|
||||
for (const ExtrusionJunction &junction : *line)
|
||||
max_line_w = std::max(max_line_w, junction.w);
|
||||
if (max_line_w == 0u)
|
||||
return order_requirements;
|
||||
|
||||
struct LineLoc
|
||||
{
|
||||
ExtrusionJunction j;
|
||||
const ExtrusionLine *line;
|
||||
};
|
||||
struct Locator
|
||||
{
|
||||
Point operator()(const LineLoc &elem) { return elem.j.p; }
|
||||
};
|
||||
|
||||
// How much farther two verts may be apart due to corners.
|
||||
// This distance must be smaller than 2, because otherwise
|
||||
// we could create an order requirement between e.g.
|
||||
// wall 2 of one region and wall 3 of another region,
|
||||
// while another wall 3 of the first region would lie in between those two walls.
|
||||
// However, higher values are better against the limitations of using a PointGrid rather than a LineGrid.
|
||||
constexpr float diagonal_extension = 1.9f;
|
||||
const auto searching_radius = coord_t(max_line_w * diagonal_extension);
|
||||
using GridT = SparsePointGrid<LineLoc, Locator>;
|
||||
GridT grid(searching_radius);
|
||||
|
||||
for (const ExtrusionLine *line : input)
|
||||
for (const ExtrusionJunction &junction : *line) grid.insert(LineLoc{junction, line});
|
||||
for (const std::pair<const SquareGrid::GridPoint, LineLoc> &pair : grid) {
|
||||
const LineLoc &lineloc_here = pair.second;
|
||||
const ExtrusionLine *here = lineloc_here.line;
|
||||
Point loc_here = pair.second.j.p;
|
||||
std::vector<LineLoc> nearby_verts = grid.getNearby(loc_here, searching_radius);
|
||||
for (const LineLoc &lineloc_nearby : nearby_verts) {
|
||||
const ExtrusionLine *nearby = lineloc_nearby.line;
|
||||
if (nearby == here)
|
||||
continue;
|
||||
if (nearby->inset_idx == here->inset_idx)
|
||||
continue;
|
||||
if (nearby->inset_idx > here->inset_idx + 1)
|
||||
continue; // not directly adjacent
|
||||
if (here->inset_idx > nearby->inset_idx + 1)
|
||||
continue; // not directly adjacent
|
||||
if (!shorter_then(loc_here - lineloc_nearby.j.p, (lineloc_here.j.w + lineloc_nearby.j.w) / 2 * diagonal_extension))
|
||||
continue; // points are too far away from each other
|
||||
if (here->is_odd || nearby->is_odd) {
|
||||
if (here->is_odd && !nearby->is_odd && nearby->inset_idx < here->inset_idx)
|
||||
order_requirements.emplace(std::make_pair(nearby, here));
|
||||
if (nearby->is_odd && !here->is_odd && here->inset_idx < nearby->inset_idx)
|
||||
order_requirements.emplace(std::make_pair(here, nearby));
|
||||
} else if ((nearby->inset_idx < here->inset_idx) == outer_to_inner) {
|
||||
order_requirements.emplace(std::make_pair(nearby, here));
|
||||
} else {
|
||||
assert((nearby->inset_idx > here->inset_idx) == outer_to_inner);
|
||||
order_requirements.emplace(std::make_pair(here, nearby));
|
||||
}
|
||||
}
|
||||
}
|
||||
return order_requirements;
|
||||
}
|
||||
|
||||
} // namespace Slic3r::Arachne
|
||||
|
|
|
|||
|
|
@ -90,6 +90,16 @@ public:
|
|||
|
||||
using ExtrusionLineSet = ankerl::unordered_dense::set<std::pair<const ExtrusionLine *, const ExtrusionLine *>, boost::hash<std::pair<const ExtrusionLine *, const ExtrusionLine *>>>;
|
||||
|
||||
/*!
|
||||
* Get the order constraints of the insets when printing walls per region / hole.
|
||||
* Each returned pair consists of adjacent wall lines where the left has an inset_idx one lower than the right.
|
||||
*
|
||||
* Odd walls should always go after their enclosing wall polygons.
|
||||
*
|
||||
* \param outer_to_inner Whether the wall polygons with a lower inset_idx should go before those with a higher one.
|
||||
*/
|
||||
static ExtrusionLineSet getRegionOrder(const std::vector<ExtrusionLine *> &input, bool outer_to_inner);
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Stitch the polylines together and form closed polygons.
|
||||
|
|
|
|||
|
|
@ -264,10 +264,9 @@ bool ExtrusionLine::is_contour() const
|
|||
return poly.is_clockwise();
|
||||
}
|
||||
|
||||
double ExtrusionLine::area() const {
|
||||
if (!this->is_closed)
|
||||
return 0.;
|
||||
|
||||
double ExtrusionLine::area() const
|
||||
{
|
||||
assert(this->is_closed);
|
||||
double a = 0.;
|
||||
if (this->junctions.size() >= 3) {
|
||||
Vec2d p1 = this->junctions.back().p.cast<double>();
|
||||
|
|
@ -277,25 +276,9 @@ double ExtrusionLine::area() const {
|
|||
p1 = p2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.5 * a;
|
||||
}
|
||||
|
||||
Points to_points(const ExtrusionLine &extrusion_line) {
|
||||
Points points;
|
||||
points.reserve(extrusion_line.junctions.size());
|
||||
for (const ExtrusionJunction &junction : extrusion_line.junctions)
|
||||
points.emplace_back(junction.p);
|
||||
return points;
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const ExtrusionLine &extrusion_line) {
|
||||
BoundingBox bbox;
|
||||
for (const ExtrusionJunction &junction : extrusion_line.junctions)
|
||||
bbox.merge(junction.p);
|
||||
return bbox;
|
||||
}
|
||||
|
||||
} // namespace Slic3r::Arachne
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
|
|||
|
|
@ -199,8 +199,6 @@ struct ExtrusionLine
|
|||
bool is_contour() const;
|
||||
|
||||
double area() const;
|
||||
|
||||
bool is_external_perimeter() const { return this->inset_idx == 0; }
|
||||
};
|
||||
|
||||
template<class PathType>
|
||||
|
|
@ -227,7 +225,6 @@ static inline Slic3r::ThickPolyline to_thick_polyline(const PathType &path)
|
|||
static inline Polygon to_polygon(const ExtrusionLine &line)
|
||||
{
|
||||
Polygon out;
|
||||
assert(line.is_closed);
|
||||
assert(line.junctions.size() >= 3);
|
||||
assert(line.junctions.front().p == line.junctions.back().p);
|
||||
out.points.reserve(line.junctions.size() - 1);
|
||||
|
|
@ -236,11 +233,24 @@ static inline Polygon to_polygon(const ExtrusionLine &line)
|
|||
return out;
|
||||
}
|
||||
|
||||
Points to_points(const ExtrusionLine &extrusion_line);
|
||||
|
||||
BoundingBox get_extents(const ExtrusionLine &extrusion_line);
|
||||
static Points to_points(const ExtrusionLine &extrusion_line)
|
||||
{
|
||||
Points points;
|
||||
points.reserve(extrusion_line.junctions.size());
|
||||
for (const ExtrusionJunction &junction : extrusion_line.junctions)
|
||||
points.emplace_back(junction.p);
|
||||
return points;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static BoundingBox get_extents(const ExtrusionLine &extrusion_line)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
for (const ExtrusionJunction &junction : extrusion_line.junctions)
|
||||
bbox.merge(junction.p);
|
||||
return bbox;
|
||||
}
|
||||
|
||||
static BoundingBox get_extents(const std::vector<ExtrusionLine> &extrusion_lines)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
|
|
@ -271,8 +281,6 @@ static std::vector<Points> to_points(const std::vector<const ExtrusionLine *> &e
|
|||
#endif
|
||||
|
||||
using VariableWidthLines = std::vector<ExtrusionLine>; //<! The ExtrusionLines generated by libArachne
|
||||
using Perimeter = VariableWidthLines;
|
||||
using Perimeters = std::vector<Perimeter>;
|
||||
|
||||
} // namespace Slic3r::Arachne
|
||||
|
||||
|
|
|
|||
|
|
@ -103,8 +103,10 @@ void update_selected_items_inflation(ArrangePolygons& selected, const DynamicPri
|
|||
BoundingBox bedbb = Polygon(bedpts).bounding_box();
|
||||
// set obj distance for auto seq_print
|
||||
if (params.is_seq_print) {
|
||||
if (params.all_objects_are_short)
|
||||
bool all_objects_are_short = std::all_of(selected.begin(), selected.end(), [&](ArrangePolygon& ap) { return ap.height < params.nozzle_height; });
|
||||
if (all_objects_are_short) {
|
||||
params.min_obj_distance = std::max(params.min_obj_distance, scaled(std::max(MAX_OUTER_NOZZLE_DIAMETER/2.f, params.object_skirt_offset*2)+0.001));
|
||||
}
|
||||
else
|
||||
params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.clearance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,6 @@ struct ArrangeParams {
|
|||
float clearance_radius = 0;
|
||||
float object_skirt_offset = 0;
|
||||
float nozzle_height = 0;
|
||||
bool all_objects_are_short = false;
|
||||
float printable_height = 256.0;
|
||||
Vec2d align_center{ 0.5,0.5 };
|
||||
|
||||
|
|
|
|||
|
|
@ -433,8 +433,6 @@ set(lisbslic3r_sources
|
|||
Arachne/utils/PolygonsSegmentIndex.hpp
|
||||
Arachne/utils/PolylineStitcher.hpp
|
||||
Arachne/utils/PolylineStitcher.cpp
|
||||
Arachne/PerimeterOrder.hpp
|
||||
Arachne/PerimeterOrder.cpp
|
||||
Arachne/SkeletalTrapezoidation.hpp
|
||||
Arachne/SkeletalTrapezoidation.cpp
|
||||
Arachne/SkeletalTrapezoidationEdge.hpp
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
|
|||
case ipTriangles: return new FillTriangles();
|
||||
case ipStars: return new FillStars();
|
||||
case ipCubic: return new FillCubic();
|
||||
case ipQuarterCubic: return new FillQuarterCubic();
|
||||
case ipArchimedeanChords: return new FillArchimedeanChords();
|
||||
case ipHilbertCurve: return new FillHilbertCurve();
|
||||
case ipOctagramSpiral: return new FillOctagramSpiral();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <boost/math/constants/constants.hpp>
|
||||
|
||||
#include "../ClipperUtils.hpp"
|
||||
#include "../ExPolygon.hpp"
|
||||
|
|
@ -3035,6 +3036,39 @@ Polylines FillCubic::fill_surface(const Surface *surface, const FillParams ¶
|
|||
return polylines_out;
|
||||
}
|
||||
|
||||
Polylines FillQuarterCubic::fill_surface(const Surface* surface, const FillParams& params)
|
||||
{
|
||||
using namespace boost::math::float_constants;
|
||||
|
||||
Polylines polylines_out;
|
||||
|
||||
coord_t line_width = coord_t(scale_(this->spacing));
|
||||
coord_t period = coord_t(scale_(this->spacing) / params.density) * 4;
|
||||
|
||||
// First half tetrahedral fill
|
||||
double pattern_z_shift = 0.0;
|
||||
coord_t shift = coord_t(one_div_root_two * (scale_(z) + pattern_z_shift * period * 2)) % period;
|
||||
shift = std::min(shift, period - shift); // symmetry due to the fact that we are applying the shift in both directions
|
||||
shift = std::min(shift, period / 2 - line_width / 2); // don't put lines too close to each other
|
||||
shift = std::max(shift, line_width / 2); // don't put lines too close to each other
|
||||
float dx1 = unscale_(shift);
|
||||
|
||||
// Second half tetrahedral fill
|
||||
pattern_z_shift = 0.5;
|
||||
shift = coord_t(one_div_root_two * (scale_(z) + pattern_z_shift * period * 2)) % period;
|
||||
shift = std::min(shift, period - shift); // symmetry due to the fact that we are applying the shift in both directions
|
||||
shift = std::min(shift, period / 2 - line_width / 2); // don't put lines too close to each other
|
||||
shift = std::max(shift, line_width / 2); // don't put lines too close to each other
|
||||
float dx2 = unscale_(shift);
|
||||
if (!this->fill_surface_by_multilines(
|
||||
surface, params,
|
||||
{{0.f, dx1}, {0.f, -dx1}, {float(M_PI / 2.), dx2}, {float(M_PI / 2.), -dx2}},
|
||||
polylines_out))
|
||||
BOOST_LOG_TRIVIAL(error) << "FillQuarterCubic::fill_surface() failed to fill a region.";
|
||||
|
||||
return polylines_out;
|
||||
}
|
||||
|
||||
Polylines FillSupportBase::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
assert(! params.full_infill());
|
||||
|
|
|
|||
|
|
@ -107,6 +107,20 @@ protected:
|
|||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
// Added QuarterCubic pattern from Cura
|
||||
class FillQuarterCubic : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillQuarterCubic(*this); }
|
||||
~FillQuarterCubic() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
|
||||
class FillSupportBase : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -197,15 +197,19 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
|
|||
}
|
||||
} else {
|
||||
TopoDS_Shape shape;
|
||||
TopExp_Explorer explorer;
|
||||
shapeTool->GetShape(referredLabel, shape);
|
||||
TopAbs_ShapeEnum shape_type = shape.ShapeType();
|
||||
BRepBuilderAPI_Transform transform(shape, localLocation, Standard_True);
|
||||
int i = 0;
|
||||
switch (shape_type) {
|
||||
case TopAbs_COMPOUND:
|
||||
namedSolids.emplace_back(TopoDS::Compound(transform.Shape()), fullName);
|
||||
break;
|
||||
case TopAbs_COMPSOLID:
|
||||
namedSolids.emplace_back(TopoDS::CompSolid(transform.Shape()), fullName);
|
||||
for (explorer.Init(transform.Shape(), TopAbs_SOLID); explorer.More(); explorer.Next()) {
|
||||
i++;
|
||||
const TopoDS_Shape& currentShape = explorer.Current();
|
||||
namedSolids.emplace_back(TopoDS::Solid(currentShape), fullName + "-SOLID-" + std::to_string(i));
|
||||
}
|
||||
break;
|
||||
case TopAbs_SOLID:
|
||||
namedSolids.emplace_back(TopoDS::Solid(transform.Shape()), fullName);
|
||||
|
|
|
|||
|
|
@ -741,6 +741,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
|||
gcodegen.config().filament_multitool_ramming.get_at(tcr.initial_tool));
|
||||
const bool should_travel_to_tower = !tcr.priming && (tcr.force_travel // wipe tower says so
|
||||
|| !needs_toolchange // this is just finishing the tower with no toolchange
|
||||
|| will_go_down // Make sure to move to prime tower before moving down
|
||||
|| is_ramming);
|
||||
|
||||
if (should_travel_to_tower || gcodegen.m_need_change_layer_lift_z) {
|
||||
|
|
@ -3823,10 +3824,16 @@ LayerResult GCode::process_layer(
|
|||
return next_extruder;
|
||||
};
|
||||
|
||||
if (m_config.enable_overhang_speed && !m_config.overhang_speed_classic) {
|
||||
for (const auto &layer_to_print : layers) {
|
||||
m_extrusion_quality_estimator.prepare_for_new_layer(layer_to_print.original_object,
|
||||
layer_to_print.object_layer);
|
||||
for (const auto &layer_to_print : layers) {
|
||||
if (layer_to_print.object_layer) {
|
||||
const auto& regions = layer_to_print.object_layer->regions();
|
||||
const bool enable_overhang_speed = std::any_of(regions.begin(), regions.end(), [](const LayerRegion* r) {
|
||||
return r->has_extrusions() && r->region().config().enable_overhang_speed && !r->region().config().overhang_speed_classic;
|
||||
});
|
||||
if (enable_overhang_speed) {
|
||||
m_extrusion_quality_estimator.prepare_for_new_layer(layer_to_print.original_object,
|
||||
layer_to_print.object_layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4190,8 +4197,11 @@ LayerResult GCode::process_layer(
|
|||
}
|
||||
}
|
||||
|
||||
if (m_config.enable_overhang_speed && !m_config.overhang_speed_classic)
|
||||
m_extrusion_quality_estimator.set_current_object(&instance_to_print.print_object);
|
||||
// Orca(#7946): set current obj regardless of the `enable_overhang_speed` value, because
|
||||
// `enable_overhang_speed` is a PrintRegionConfig and here we don't have a region yet.
|
||||
// And no side effect doing this even if `enable_overhang_speed` is off, so don't bother
|
||||
// checking anything here.
|
||||
m_extrusion_quality_estimator.set_current_object(&instance_to_print.print_object);
|
||||
|
||||
// When starting a new object, use the external motion planner for the first travel move.
|
||||
const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
|
||||
|
|
@ -4264,7 +4274,6 @@ LayerResult GCode::process_layer(
|
|||
m_last_obj_copy = this_object_copy;
|
||||
this->set_origin(unscale(offset));
|
||||
//FIXME the following code prints regions in the order they are defined, the path is not optimized in any way.
|
||||
bool is_infill_first =m_config.is_infill_first;
|
||||
|
||||
auto has_infill = [](const std::vector<ObjectByExtruder::Island::Region> &by_region) {
|
||||
for (auto region : by_region) {
|
||||
|
|
@ -4273,10 +4282,9 @@ LayerResult GCode::process_layer(
|
|||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
//BBS: for first layer, we always print wall firstly to get better bed adhesive force
|
||||
//This behaviour is same with cura
|
||||
if (is_infill_first && !first_layer) {
|
||||
{
|
||||
// Print perimeters of regions that has is_infill_first == false
|
||||
gcode += this->extrude_perimeters(print, by_region_specific, first_layer, false);
|
||||
if (!has_wipe_tower && need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode && has_infill(by_region_specific)) {
|
||||
gcode += this->retract(false, false, LiftType::NormalLift);
|
||||
|
||||
|
|
@ -4293,27 +4301,10 @@ LayerResult GCode::process_layer(
|
|||
|
||||
has_insert_timelapse_gcode = true;
|
||||
}
|
||||
// Then print infill
|
||||
gcode += this->extrude_infill(print, by_region_specific, false);
|
||||
gcode += this->extrude_perimeters(print, by_region_specific);
|
||||
} else {
|
||||
gcode += this->extrude_perimeters(print, by_region_specific);
|
||||
if (!has_wipe_tower && need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode && has_infill(by_region_specific)) {
|
||||
gcode += this->retract(false, false, LiftType::NormalLift);
|
||||
|
||||
std::string timepals_gcode = insert_timelapse_gcode();
|
||||
gcode += timepals_gcode;
|
||||
m_writer.set_current_position_clear(false);
|
||||
//BBS: check whether custom gcode changes the z position. Update if changed
|
||||
double temp_z_after_timepals_gcode;
|
||||
if (GCodeProcessor::get_last_z_from_gcode(timepals_gcode, temp_z_after_timepals_gcode)) {
|
||||
Vec3d pos = m_writer.get_position();
|
||||
pos(2) = temp_z_after_timepals_gcode;
|
||||
m_writer.set_position(pos);
|
||||
}
|
||||
|
||||
has_insert_timelapse_gcode = true;
|
||||
}
|
||||
gcode += this->extrude_infill(print,by_region_specific, false);
|
||||
// Then print perimeters of regions that has is_infill_first == true
|
||||
gcode += this->extrude_perimeters(print, by_region_specific, first_layer, true);
|
||||
}
|
||||
// ironing
|
||||
gcode += this->extrude_infill(print,by_region_specific, true);
|
||||
|
|
@ -4911,12 +4902,17 @@ std::string GCode::extrude_path(ExtrusionPath path, std::string description, dou
|
|||
}
|
||||
|
||||
// Extrude perimeters: Decide where to put seams (hide or align seams).
|
||||
std::string GCode::extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region)
|
||||
std::string GCode::extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, bool is_first_layer, bool is_infill_first)
|
||||
{
|
||||
std::string gcode;
|
||||
for (const ObjectByExtruder::Island::Region ®ion : by_region)
|
||||
if (! region.perimeters.empty()) {
|
||||
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
||||
// BBS: for first layer, we always print wall firstly to get better bed adhesive force
|
||||
// This behaviour is same with cura
|
||||
const bool should_print = is_first_layer ? !is_infill_first
|
||||
: (m_config.is_infill_first == is_infill_first);
|
||||
if (!should_print) continue;
|
||||
|
||||
for (const ExtrusionEntity* ee : region.perimeters)
|
||||
gcode += this->extrude_entity(*ee, "perimeter", -1., region.perimeters);
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@ private:
|
|||
// For sequential print, the instance of the object to be printing has to be defined.
|
||||
const size_t single_object_instance_idx);
|
||||
|
||||
std::string extrude_perimeters(const Print& print, const std::vector<ObjectByExtruder::Island::Region>& by_region);
|
||||
std::string extrude_perimeters(const Print& print, const std::vector<ObjectByExtruder::Island::Region>& by_region, bool is_first_layer, bool is_infill_first);
|
||||
std::string extrude_infill(const Print& print, const std::vector<ObjectByExtruder::Island::Region>& by_region, bool ironing);
|
||||
std::string extrude_support(const ExtrusionEntityCollection& support_fills);
|
||||
|
||||
|
|
|
|||
|
|
@ -4918,10 +4918,13 @@ void GCodeProcessor::run_post_process()
|
|||
// add lines M104 to exported gcode
|
||||
auto process_line_T = [this, &export_lines](const std::string& gcode_line, const size_t g1_lines_counter, const ExportLines::Backtrace& backtrace) {
|
||||
const std::string cmd = GCodeReader::GCodeLine::extract_cmd(gcode_line);
|
||||
|
||||
int tool_number = -1;
|
||||
if (!parse_number(std::string_view(cmd).substr(1), tool_number)){
|
||||
// invalid T<n> command, such as the "TIMELAPSE_TAKE_FRAME" gcode, just ignore
|
||||
return;
|
||||
}
|
||||
if (cmd.size() >= 2) {
|
||||
std::stringstream ss(cmd.substr(1));
|
||||
int tool_number = -1;
|
||||
ss >> tool_number;
|
||||
if (tool_number != -1) {
|
||||
if (tool_number < 0 || (int)m_extruder_temps_config.size() <= tool_number) {
|
||||
// found an invalid value, clamp it to a valid one
|
||||
|
|
@ -5018,9 +5021,18 @@ void GCodeProcessor::run_post_process()
|
|||
// End of line is indicated also if end of file was reached.
|
||||
eol |= eof && it_end == it_bufend;
|
||||
gcode_line.insert(gcode_line.end(), it, it_end);
|
||||
|
||||
it = it_end;
|
||||
// append EOL.
|
||||
if (it != it_bufend && *it == '\r') {
|
||||
gcode_line += *it++;
|
||||
}
|
||||
if (it != it_bufend && *it == '\n') {
|
||||
gcode_line += *it++;
|
||||
}
|
||||
|
||||
if (eol) {
|
||||
++line_id;
|
||||
gcode_line += "\n";
|
||||
const unsigned int internal_g1_lines_counter = export_lines.update(gcode_line, line_id, g1_lines_counter);
|
||||
// replace placeholder lines
|
||||
bool processed = process_placeholders(gcode_line);
|
||||
|
|
@ -5057,12 +5069,6 @@ void GCodeProcessor::run_post_process()
|
|||
export_lines.write(out, 1.1f * max_backtrace_time, m_result, out_path);
|
||||
gcode_line.clear();
|
||||
}
|
||||
// Skip EOL.
|
||||
it = it_end;
|
||||
if (it != it_bufend && *it == '\r')
|
||||
++it;
|
||||
if (it != it_bufend && *it == '\n')
|
||||
++it;
|
||||
}
|
||||
if (eof)
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -206,6 +206,7 @@ static double calc_max_layer_height(const PrintConfig &config, double max_object
|
|||
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
|
||||
{
|
||||
m_is_BBL_printer = object.print()->is_BBL_printer();
|
||||
m_print_full_config = &object.print()->full_print_config();
|
||||
m_print_object_ptr = &object;
|
||||
if (object.layers().empty())
|
||||
return;
|
||||
|
|
@ -250,6 +251,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
|
|||
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
|
||||
{
|
||||
m_is_BBL_printer = print.is_BBL_printer();
|
||||
m_print_full_config = &print.full_print_config();
|
||||
m_print_config_ptr = &print.config();
|
||||
|
||||
// Initialize the print layers for all objects and all layers.
|
||||
|
|
@ -315,6 +317,24 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
|
|||
this->mark_skirt_layers(print.config(), max_layer_height);
|
||||
}
|
||||
|
||||
static void apply_first_layer_order(const DynamicPrintConfig* config, std::vector<unsigned int>& tool_order) {
|
||||
const ConfigOptionInts* first_layer_print_sequence_op = config->option<ConfigOptionInts>("first_layer_print_sequence");
|
||||
if (first_layer_print_sequence_op) {
|
||||
const std::vector<int>& print_sequence_1st = first_layer_print_sequence_op->values;
|
||||
if (print_sequence_1st.size() >= tool_order.size()) {
|
||||
std::sort(tool_order.begin(), tool_order.end(), [&print_sequence_1st](int lh, int rh) {
|
||||
auto lh_it = std::find(print_sequence_1st.begin(), print_sequence_1st.end(), lh);
|
||||
auto rh_it = std::find(print_sequence_1st.begin(), print_sequence_1st.end(), rh);
|
||||
|
||||
if (lh_it == print_sequence_1st.end() || rh_it == print_sequence_1st.end())
|
||||
return false;
|
||||
|
||||
return lh_it < rh_it;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
std::vector<unsigned int> ToolOrdering::generate_first_layer_tool_order(const Print& print)
|
||||
{
|
||||
|
|
@ -359,21 +379,7 @@ std::vector<unsigned int> ToolOrdering::generate_first_layer_tool_order(const Pr
|
|||
tool_order.insert(iter, ape.first);
|
||||
}
|
||||
|
||||
const ConfigOptionInts* first_layer_print_sequence_op = print.full_print_config().option<ConfigOptionInts>("first_layer_print_sequence");
|
||||
if (first_layer_print_sequence_op) {
|
||||
const std::vector<int>& print_sequence_1st = first_layer_print_sequence_op->values;
|
||||
if (print_sequence_1st.size() >= tool_order.size()) {
|
||||
std::sort(tool_order.begin(), tool_order.end(), [&print_sequence_1st](int lh, int rh) {
|
||||
auto lh_it = std::find(print_sequence_1st.begin(), print_sequence_1st.end(), lh);
|
||||
auto rh_it = std::find(print_sequence_1st.begin(), print_sequence_1st.end(), rh);
|
||||
|
||||
if (lh_it == print_sequence_1st.end() || rh_it == print_sequence_1st.end())
|
||||
return false;
|
||||
|
||||
return lh_it < rh_it;
|
||||
});
|
||||
}
|
||||
}
|
||||
apply_first_layer_order(m_print_full_config, tool_order);
|
||||
|
||||
return tool_order;
|
||||
}
|
||||
|
|
@ -417,21 +423,7 @@ std::vector<unsigned int> ToolOrdering::generate_first_layer_tool_order(const Pr
|
|||
tool_order.insert(iter, ape.first);
|
||||
}
|
||||
|
||||
const ConfigOptionInts* first_layer_print_sequence_op = object.print()->full_print_config().option<ConfigOptionInts>("first_layer_print_sequence");
|
||||
if (first_layer_print_sequence_op) {
|
||||
const std::vector<int>& print_sequence_1st = first_layer_print_sequence_op->values;
|
||||
if (print_sequence_1st.size() >= tool_order.size()) {
|
||||
std::sort(tool_order.begin(), tool_order.end(), [&print_sequence_1st](int lh, int rh) {
|
||||
auto lh_it = std::find(print_sequence_1st.begin(), print_sequence_1st.end(), lh);
|
||||
auto rh_it = std::find(print_sequence_1st.begin(), print_sequence_1st.end(), rh);
|
||||
|
||||
if (lh_it == print_sequence_1st.end() || rh_it == print_sequence_1st.end())
|
||||
return false;
|
||||
|
||||
return lh_it < rh_it;
|
||||
});
|
||||
}
|
||||
}
|
||||
apply_first_layer_order(m_print_full_config, tool_order);
|
||||
|
||||
return tool_order;
|
||||
}
|
||||
|
|
@ -606,14 +598,19 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
|
|||
break;
|
||||
}
|
||||
|
||||
// On first layer with wipe tower, prefer a soluble extruder
|
||||
// at the beginning, so it is not wiped on the first layer.
|
||||
if (lt == m_layer_tools[0] && m_print_config_ptr && m_print_config_ptr->enable_prime_tower) {
|
||||
for (size_t i = 0; i<lt.extruders.size(); ++i)
|
||||
if (m_print_config_ptr->filament_soluble.get_at(lt.extruders[i]-1)) { // 1-based...
|
||||
std::swap(lt.extruders[i], lt.extruders.front());
|
||||
break;
|
||||
}
|
||||
if (lt == m_layer_tools[0]) {
|
||||
// On first layer with wipe tower, prefer a soluble extruder
|
||||
// at the beginning, so it is not wiped on the first layer.
|
||||
if (m_print_config_ptr && m_print_config_ptr->enable_prime_tower) {
|
||||
for (size_t i = 0; i<lt.extruders.size(); ++i)
|
||||
if (m_print_config_ptr->filament_soluble.get_at(lt.extruders[i]-1)) { // 1-based...
|
||||
std::swap(lt.extruders[i], lt.extruders.front());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Then, if we specified the tool order, apply it now
|
||||
apply_first_layer_order(m_print_full_config, lt.extruders);
|
||||
}
|
||||
}
|
||||
last_extruder_id = lt.extruders.back();
|
||||
|
|
@ -1005,13 +1002,17 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
|
|||
bool tool_changes_as_color_changes = mode == CustomGCode::SingleExtruder && model_mode == CustomGCode::MultiAsSingle;
|
||||
|
||||
// From the last layer to the first one:
|
||||
coordf_t print_z_above = std::numeric_limits<coordf_t>::lowest();
|
||||
for (auto it_lt = m_layer_tools.rbegin(); it_lt != m_layer_tools.rend(); ++ it_lt) {
|
||||
LayerTools < = *it_lt;
|
||||
// Add the extruders of the current layer to the set of extruders printing at and above this print_z.
|
||||
for (unsigned int i : lt.extruders)
|
||||
extruder_printing_above[i] = true;
|
||||
// Skip all custom G-codes above this layer and skip all extruder switches.
|
||||
for (; custom_gcode_it != custom_gcode_per_print_z.gcodes.rend() && (custom_gcode_it->print_z > lt.print_z + EPSILON || custom_gcode_it->type == CustomGCode::ToolChange); ++ custom_gcode_it);
|
||||
for (; custom_gcode_it != custom_gcode_per_print_z.gcodes.rend() && (
|
||||
(print_z_above > lt.print_z && custom_gcode_it->print_z > 0.5 * (lt.print_z + print_z_above))
|
||||
|| custom_gcode_it->type == CustomGCode::ToolChange); ++ custom_gcode_it);
|
||||
print_z_above = lt.print_z;
|
||||
if (custom_gcode_it == custom_gcode_per_print_z.gcodes.rend())
|
||||
// Custom G-codes were processed.
|
||||
break;
|
||||
|
|
@ -1021,7 +1022,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
|
|||
coordf_t print_z_below = 0.;
|
||||
if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend())
|
||||
print_z_below = it_lt_below->print_z;
|
||||
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
|
||||
if (custom_gcode.print_z > 0.5 * (print_z_below + lt.print_z)) {
|
||||
// The custom G-code applies to the current layer.
|
||||
bool color_change = custom_gcode.type == CustomGCode::ColorChange;
|
||||
bool tool_change = custom_gcode.type == CustomGCode::ToolChange;
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ private:
|
|||
// All extruders, which extrude some material over m_layer_tools.
|
||||
std::vector<unsigned int> m_all_printing_extruders;
|
||||
std::unordered_map<uint32_t, std::vector<uint8_t>> m_tool_order_cache;
|
||||
const DynamicPrintConfig* m_print_full_config = nullptr;
|
||||
const PrintConfig* m_print_config_ptr = nullptr;
|
||||
const PrintObject* m_print_object_ptr = nullptr;
|
||||
bool m_is_BBL_printer = false;
|
||||
|
|
|
|||
|
|
@ -168,6 +168,8 @@ void Layer::make_perimeters()
|
|||
const PrintRegionConfig &other_config = other_layerm->region().config();
|
||||
if (config.wall_filament == other_config.wall_filament
|
||||
&& config.wall_loops == other_config.wall_loops
|
||||
&& config.wall_sequence == other_config.wall_sequence
|
||||
&& config.is_infill_first == other_config.is_infill_first
|
||||
&& config.inner_wall_speed == other_config.inner_wall_speed
|
||||
&& config.outer_wall_speed == other_config.outer_wall_speed
|
||||
&& config.small_perimeter_speed == other_config.small_perimeter_speed
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -318,17 +318,27 @@ std::string Preset::remove_suffix_modified(const std::string &name)
|
|||
// Update new extruder fields at the printer profile.
|
||||
void Preset::normalize(DynamicPrintConfig &config)
|
||||
{
|
||||
// BBS
|
||||
auto* filament_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("filament_diameter"));
|
||||
if (filament_diameter != nullptr)
|
||||
// Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values.
|
||||
config.set_num_filaments((unsigned int)filament_diameter->values.size());
|
||||
size_t n = 1;
|
||||
if (config.option("single_extruder_multi_material") == nullptr || config.opt_bool("single_extruder_multi_material")) {
|
||||
// BBS
|
||||
auto* filament_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("filament_diameter"));
|
||||
if (filament_diameter != nullptr) {
|
||||
n = filament_diameter->values.size();
|
||||
// Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values.
|
||||
config.set_num_filaments((unsigned int) n);
|
||||
}
|
||||
} else {
|
||||
auto* nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
|
||||
if (nozzle_diameter != nullptr) {
|
||||
n = nozzle_diameter->values.size();
|
||||
// Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values.
|
||||
config.set_num_extruders((unsigned int) n);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.option("filament_diameter") != nullptr) {
|
||||
// This config contains single or multiple filament presets.
|
||||
// Ensure that the filament preset vector options contain the correct number of values.
|
||||
// BBS
|
||||
size_t n = (filament_diameter == nullptr) ? 1 : filament_diameter->values.size();
|
||||
const auto &defaults = FullPrintConfig::defaults();
|
||||
for (const std::string &key : Preset::filament_options()) {
|
||||
if (key == "compatible_prints" || key == "compatible_printers")
|
||||
|
|
|
|||
|
|
@ -143,7 +143,8 @@ static t_config_enum_values s_keys_map_InfillPattern {
|
|||
{ "octagramspiral", ipOctagramSpiral },
|
||||
{ "supportcubic", ipSupportCubic },
|
||||
{ "lightning", ipLightning },
|
||||
{ "crosshatch", ipCrossHatch}
|
||||
{ "crosshatch", ipCrossHatch},
|
||||
{ "quartercubic", ipQuarterCubic}
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
|
||||
|
||||
|
|
@ -362,7 +363,7 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BedType)
|
|||
// BBS
|
||||
static const t_config_enum_values s_keys_map_LayerSeq = {
|
||||
{ "Auto", flsAuto },
|
||||
{ "Customize", flsCutomize },
|
||||
{ "Customize", flsCustomize },
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(LayerSeq)
|
||||
|
||||
|
|
@ -2272,6 +2273,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("supportcubic");
|
||||
def->enum_values.push_back("lightning");
|
||||
def->enum_values.push_back("crosshatch");
|
||||
def->enum_values.push_back("quartercubic");
|
||||
def->enum_labels.push_back(L("Concentric"));
|
||||
def->enum_labels.push_back(L("Rectilinear"));
|
||||
def->enum_labels.push_back(L("Grid"));
|
||||
|
|
@ -2290,6 +2292,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back(L("Support Cubic"));
|
||||
def->enum_labels.push_back(L("Lightning"));
|
||||
def->enum_labels.push_back(L("Cross Hatch"));
|
||||
def->enum_labels.push_back(L("Quarter Cubic"));
|
||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipCrossHatch));
|
||||
|
||||
auto def_infill_anchor_min = def = this->add("infill_anchor", coFloatOrPercent);
|
||||
|
|
@ -4406,17 +4409,17 @@ void PrintConfigDef::init_fff_params()
|
|||
def = this->add("support_type", coEnum);
|
||||
def->label = L("Type");
|
||||
def->category = L("Support");
|
||||
def->tooltip = L("normal(auto) and tree(auto) is used to generate support automatically. "
|
||||
"If normal(manual) or tree(manual) is selected, only support enforcers are generated");
|
||||
def->tooltip = L("Normal (auto) and Tree (auto) is used to generate support automatically. "
|
||||
"If Normal (manual) or Tree (manual) is selected, only support enforcers are generated");
|
||||
def->enum_keys_map = &ConfigOptionEnum<SupportType>::get_enum_values();
|
||||
def->enum_values.push_back("normal(auto)");
|
||||
def->enum_values.push_back("tree(auto)");
|
||||
def->enum_values.push_back("normal(manual)");
|
||||
def->enum_values.push_back("tree(manual)");
|
||||
def->enum_labels.push_back(L("normal(auto)"));
|
||||
def->enum_labels.push_back(L("tree(auto)"));
|
||||
def->enum_labels.push_back(L("normal(manual)"));
|
||||
def->enum_labels.push_back(L("tree(manual)"));
|
||||
def->enum_labels.push_back(L("Normal (auto)"));
|
||||
def->enum_labels.push_back(L("Tree (auto)"));
|
||||
def->enum_labels.push_back(L("Normal (manual)"));
|
||||
def->enum_labels.push_back(L("Tree (manual)"));
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionEnum<SupportType>(stNormalAuto));
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ enum AuthorizationType {
|
|||
enum InfillPattern : int {
|
||||
ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
|
||||
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
|
||||
ipLightning, ipCrossHatch,
|
||||
ipLightning, ipCrossHatch, ipQuarterCubic,
|
||||
ipCount,
|
||||
};
|
||||
|
||||
|
|
@ -266,7 +266,7 @@ enum BedType {
|
|||
// BBS
|
||||
enum LayerSeq {
|
||||
flsAuto,
|
||||
flsCutomize
|
||||
flsCustomize
|
||||
};
|
||||
|
||||
// BBS
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,8 @@
|
|||
#include "Flow.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Slicing.hpp"
|
||||
|
||||
#include "Fill/FillBase.hpp"
|
||||
#include "SupportLayer.hpp"
|
||||
namespace Slic3r {
|
||||
|
||||
class PrintObject;
|
||||
|
|
@ -18,107 +19,6 @@ class PrintObjectConfig;
|
|||
class PrintObjectSupportMaterial
|
||||
{
|
||||
public:
|
||||
// Support layer type to be used by MyLayer. This type carries a much more detailed information
|
||||
// about the support layer type than the final support layers stored in a PrintObject.
|
||||
enum SupporLayerType {
|
||||
sltUnknown = 0,
|
||||
// Ratft base layer, to be printed with the support material.
|
||||
sltRaftBase,
|
||||
// Raft interface layer, to be printed with the support interface material.
|
||||
sltRaftInterface,
|
||||
// Bottom contact layer placed over a top surface of an object. To be printed with a support interface material.
|
||||
sltBottomContact,
|
||||
// Dense interface layer, to be printed with the support interface material.
|
||||
// This layer is separated from an object by an sltBottomContact layer.
|
||||
sltBottomInterface,
|
||||
// Sparse base support layer, to be printed with a support material.
|
||||
sltBase,
|
||||
// Dense interface layer, to be printed with the support interface material.
|
||||
// This layer is separated from an object with sltTopContact layer.
|
||||
sltTopInterface,
|
||||
// Top contact layer directly supporting an overhang. To be printed with a support interface material.
|
||||
sltTopContact,
|
||||
// Some undecided type yet. It will turn into sltBase first, then it may turn into sltBottomInterface or sltTopInterface.
|
||||
sltIntermediate,
|
||||
};
|
||||
|
||||
// A support layer type used internally by the SupportMaterial class. This class carries a much more detailed
|
||||
// information about the support layer than the layers stored in the PrintObject, mainly
|
||||
// the MyLayer is aware of the bridging flow and the interface gaps between the object and the support.
|
||||
class MyLayer
|
||||
{
|
||||
public:
|
||||
void reset() {
|
||||
*this = MyLayer();
|
||||
}
|
||||
|
||||
bool operator==(const MyLayer &layer2) const {
|
||||
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
|
||||
}
|
||||
|
||||
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
|
||||
bool operator<(const MyLayer &layer2) const {
|
||||
if (print_z < layer2.print_z) {
|
||||
return true;
|
||||
} else if (print_z == layer2.print_z) {
|
||||
if (height > layer2.height)
|
||||
return true;
|
||||
else if (height == layer2.height) {
|
||||
// Bridging layers first.
|
||||
return bridging && ! layer2.bridging;
|
||||
} else
|
||||
return false;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
void merge(MyLayer &&rhs) {
|
||||
// The union_() does not support move semantic yet, but maybe one day it will.
|
||||
this->polygons = union_(this->polygons, std::move(rhs.polygons));
|
||||
auto merge = [](std::unique_ptr<Polygons> &dst, std::unique_ptr<Polygons> &src) {
|
||||
if (! dst || dst->empty())
|
||||
dst = std::move(src);
|
||||
else if (src && ! src->empty())
|
||||
*dst = union_(*dst, std::move(*src));
|
||||
};
|
||||
merge(this->contact_polygons, rhs.contact_polygons);
|
||||
merge(this->overhang_polygons, rhs.overhang_polygons);
|
||||
merge(this->enforcer_polygons, rhs.enforcer_polygons);
|
||||
rhs.reset();
|
||||
}
|
||||
|
||||
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
|
||||
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
|
||||
coordf_t bottom_print_z() const { return print_z - height; }
|
||||
|
||||
// To sort the extremes of top / bottom interface layers.
|
||||
coordf_t extreme_z() const { return (this->layer_type == sltTopContact) ? this->bottom_z : this->print_z; }
|
||||
|
||||
SupporLayerType layer_type { sltUnknown };
|
||||
// Z used for printing, in unscaled coordinates.
|
||||
coordf_t print_z { 0 };
|
||||
// Bottom Z of this layer. For soluble layers, bottom_z + height = print_z,
|
||||
// otherwise bottom_z + gap + height = print_z.
|
||||
coordf_t bottom_z { 0 };
|
||||
// Layer height in unscaled coordinates.
|
||||
coordf_t height { 0 };
|
||||
// Index of a PrintObject layer_id supported by this layer. This will be set for top contact layers.
|
||||
// If this is not a contact layer, it will be set to size_t(-1).
|
||||
size_t idx_object_layer_above { size_t(-1) };
|
||||
// Index of a PrintObject layer_id, which supports this layer. This will be set for bottom contact layers.
|
||||
// If this is not a contact layer, it will be set to size_t(-1).
|
||||
size_t idx_object_layer_below { size_t(-1) };
|
||||
// Use a bridging flow when printing this support layer.
|
||||
bool bridging { false };
|
||||
|
||||
// Polygons to be filled by the support pattern.
|
||||
Polygons polygons;
|
||||
// Currently for the contact layers only.
|
||||
std::unique_ptr<Polygons> contact_polygons;
|
||||
std::unique_ptr<Polygons> overhang_polygons;
|
||||
// Enforcers need to be propagated independently in case the "support on build plate only" option is enabled.
|
||||
std::unique_ptr<Polygons> enforcer_polygons;
|
||||
};
|
||||
|
||||
struct SupportParams {
|
||||
Flow first_layer_flow;
|
||||
|
|
@ -147,12 +47,6 @@ public:
|
|||
bool with_sheath;
|
||||
};
|
||||
|
||||
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained
|
||||
// up to the end of a generate() method. The layer storage may be replaced by an allocator class in the future,
|
||||
// which would allocate layers by multiple chunks.
|
||||
typedef std::deque<MyLayer> MyLayerStorage;
|
||||
typedef std::vector<MyLayer*> MyLayersPtr;
|
||||
|
||||
public:
|
||||
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
|
||||
|
||||
|
|
@ -176,58 +70,58 @@ private:
|
|||
// Generate top contact layers supporting overhangs.
|
||||
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
|
||||
// If supports over bed surface only are requested, don't generate contact layers over an object.
|
||||
MyLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, MyLayerStorage &layer_storage) const;
|
||||
SupportGeneratorLayersPtr top_contact_layers(const PrintObject &object, const std::vector<Polygons> &buildplate_covered, SupportGeneratorLayerStorage &layer_storage) const;
|
||||
|
||||
// Generate bottom contact layers supporting the top contact layers.
|
||||
// For a soluble interface material synchronize the layer heights with the object,
|
||||
// otherwise set the layer height to a bridging flow of a support interface nozzle.
|
||||
MyLayersPtr bottom_contact_layers_and_layer_support_areas(
|
||||
const PrintObject &object, const MyLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
|
||||
MyLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
|
||||
SupportGeneratorLayersPtr bottom_contact_layers_and_layer_support_areas(
|
||||
const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector<Polygons> &buildplate_covered,
|
||||
SupportGeneratorLayerStorage &layer_storage, std::vector<Polygons> &layer_support_areas) const;
|
||||
|
||||
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
|
||||
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const;
|
||||
void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const SupportGeneratorLayersPtr &bottom_contacts, SupportGeneratorLayersPtr &top_contacts) const;
|
||||
|
||||
// Generate raft layers and the intermediate support layers between the bottom contact and top contact surfaces.
|
||||
MyLayersPtr raft_and_intermediate_support_layers(
|
||||
SupportGeneratorLayersPtr raft_and_intermediate_support_layers(
|
||||
const PrintObject &object,
|
||||
const MyLayersPtr &bottom_contacts,
|
||||
const MyLayersPtr &top_contacts,
|
||||
MyLayerStorage &layer_storage) const;
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
SupportGeneratorLayerStorage &layer_storage) const;
|
||||
|
||||
// Fill in the base layers with polygons.
|
||||
void generate_base_layers(
|
||||
const PrintObject &object,
|
||||
const MyLayersPtr &bottom_contacts,
|
||||
const MyLayersPtr &top_contacts,
|
||||
MyLayersPtr &intermediate_layers,
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
SupportGeneratorLayersPtr &intermediate_layers,
|
||||
const std::vector<Polygons> &layer_support_areas) const;
|
||||
|
||||
// Generate raft layers, also expand the 1st support layer
|
||||
// in case there is no raft layer to improve support adhesion.
|
||||
MyLayersPtr generate_raft_base(
|
||||
SupportGeneratorLayersPtr generate_raft_base(
|
||||
const PrintObject &object,
|
||||
const MyLayersPtr &top_contacts,
|
||||
const MyLayersPtr &interface_layers,
|
||||
const MyLayersPtr &base_interface_layers,
|
||||
const MyLayersPtr &base_layers,
|
||||
MyLayerStorage &layer_storage) const;
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
const SupportGeneratorLayersPtr &interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage) const;
|
||||
|
||||
// Turn some of the base layers into base interface layers.
|
||||
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
|
||||
// extruder to improve adhesion of the soluble filament to the base.
|
||||
std::pair<MyLayersPtr, MyLayersPtr> generate_interface_layers(
|
||||
const MyLayersPtr &bottom_contacts,
|
||||
const MyLayersPtr &top_contacts,
|
||||
MyLayersPtr &intermediate_layers,
|
||||
MyLayerStorage &layer_storage) const;
|
||||
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
SupportGeneratorLayersPtr &intermediate_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage) const;
|
||||
|
||||
|
||||
// Trim support layers by an object to leave a defined gap between
|
||||
// the support volume and the object.
|
||||
void trim_support_layers_by_object(
|
||||
const PrintObject &object,
|
||||
MyLayersPtr &support_layers,
|
||||
SupportGeneratorLayersPtr &support_layers,
|
||||
const coordf_t gap_extra_above,
|
||||
const coordf_t gap_extra_below,
|
||||
const coordf_t gap_xy) const;
|
||||
|
|
@ -240,12 +134,12 @@ private:
|
|||
// Produce the actual G-code.
|
||||
void generate_toolpaths(
|
||||
SupportLayerPtrs &support_layers,
|
||||
const MyLayersPtr &raft_layers,
|
||||
const MyLayersPtr &bottom_contacts,
|
||||
const MyLayersPtr &top_contacts,
|
||||
const MyLayersPtr &intermediate_layers,
|
||||
const MyLayersPtr &interface_layers,
|
||||
const MyLayersPtr &base_interface_layers) const;
|
||||
const SupportGeneratorLayersPtr &raft_layers,
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
const SupportGeneratorLayersPtr &intermediate_layers,
|
||||
const SupportGeneratorLayersPtr &interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_interface_layers) const;
|
||||
|
||||
// Following objects are not owned by SupportMaterial class.
|
||||
const PrintObject *m_object;
|
||||
|
|
|
|||
|
|
@ -1064,16 +1064,7 @@ void SpinCtrl::BUILD() {
|
|||
break;
|
||||
}
|
||||
|
||||
const int min_val = m_opt.min == INT_MIN
|
||||
#ifdef __WXOSX__
|
||||
// We will forcibly set the input value for SpinControl, since the value
|
||||
// inserted from the keyboard is not updated under OSX.
|
||||
// So, we can't set min control value bigger then 0.
|
||||
// Otherwise, it couldn't be possible to input from keyboard value
|
||||
// less then min_val.
|
||||
|| m_opt.min > 0
|
||||
#endif
|
||||
? 0 : m_opt.min;
|
||||
const int min_val = m_opt.min == INT_MIN ? 0 : m_opt.min;
|
||||
const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647;
|
||||
|
||||
static Builder<SpinInput> builder;
|
||||
|
|
@ -1170,14 +1161,6 @@ void SpinCtrl::propagate_value()
|
|||
if (!m_value.empty()) // BBS: null value
|
||||
on_kill_focus();
|
||||
} else {
|
||||
#ifdef __WXOSX__
|
||||
// check input value for minimum
|
||||
if (m_opt.min > 0 && tmp_value < m_opt.min) {
|
||||
SpinInput* spin = static_cast<SpinInput*>(window);
|
||||
spin->SetValue(m_opt.min);
|
||||
// spin->GetText()->SetInsertionPointEnd(); // BBS
|
||||
}
|
||||
#endif
|
||||
auto ctrl = dynamic_cast<SpinInput *>(window);
|
||||
if (m_value.empty()
|
||||
? !ctrl->GetTextCtrl()->GetLabel().IsEmpty()
|
||||
|
|
|
|||
|
|
@ -6767,7 +6767,7 @@ void GLCanvas3D::_picking_pass()
|
|||
case SceneRaycaster::EType::Bed:
|
||||
{
|
||||
// BBS: add plate picking logic
|
||||
int plate_hover_id = PartPlate::PLATE_BASE_ID - hit.raycaster_id;
|
||||
int plate_hover_id = hit.raycaster_id;
|
||||
if (plate_hover_id >= 0 && plate_hover_id < PartPlateList::MAX_PLATES_COUNT * PartPlate::GRABBER_COUNT) {
|
||||
wxGetApp().plater()->get_partplate_list().set_hover_id(plate_hover_id);
|
||||
m_hover_plate_idxs.emplace_back(plate_hover_id);
|
||||
|
|
|
|||
|
|
@ -3598,7 +3598,7 @@ GuiCfg create_gui_configuration()
|
|||
float space = line_height_with_spacing - line_height;
|
||||
const ImGuiStyle &style = ImGui::GetStyle();
|
||||
|
||||
cfg.max_style_name_width = ImGui::CalcTextSize("Maximal font name, extended").x;
|
||||
cfg.max_style_name_width = ImGui::CalcTextSize("Maximal style name..").x;
|
||||
|
||||
cfg.icon_width = static_cast<unsigned int>(std::ceil(line_height));
|
||||
// make size pair number
|
||||
|
|
@ -3692,11 +3692,11 @@ GuiCfg create_gui_configuration()
|
|||
// "Text is to object" + radio buttons
|
||||
cfg.height_of_volume_type_selector = separator_height + line_height_with_spacing + input_height;
|
||||
|
||||
int max_style_image_width = static_cast<int>(std::round(cfg.max_style_name_width/2 - 2 * style.FramePadding.x));
|
||||
int max_style_image_width = static_cast<int>(std::round(cfg.max_style_name_width - 2 * style.FramePadding.x));
|
||||
int max_style_image_height = static_cast<int>(std::round(input_height));
|
||||
cfg.max_style_image_size = Vec2i32(max_style_image_width, line_height);
|
||||
cfg.face_name_size = Vec2i32(cfg.input_width, line_height_with_spacing);
|
||||
cfg.face_name_texture_offset_x = cfg.face_name_size.x() + space;
|
||||
cfg.face_name_texture_offset_x = cfg.face_name_size.x() + style.WindowPadding.x + space;
|
||||
|
||||
cfg.max_tooltip_width = ImGui::GetFontSize() * 20.0f;
|
||||
|
||||
|
|
|
|||
|
|
@ -375,6 +375,9 @@ void GLGizmosManager::update_data()
|
|||
: CommonGizmosDataID(0));
|
||||
if (m_current != Undefined) m_gizmos[m_current]->data_changed(m_serializing);
|
||||
|
||||
// Orca: hack: Fix issue that flatten gizmo faces not updated after reload from disk
|
||||
if (m_current != Flatten && !m_gizmos.empty()) m_gizmos[Flatten]->data_changed(m_serializing);
|
||||
|
||||
//BBS: GUI refactor: add object manipulation in gizmo
|
||||
m_object_manipulation.update_ui_from_settings();
|
||||
m_object_manipulation.UpdateAndShow(true);
|
||||
|
|
|
|||
|
|
@ -775,7 +775,6 @@ arrangement::ArrangeParams init_arrange_params(Plater *p)
|
|||
params.printable_height = print_config.printable_height.value;
|
||||
params.allow_rotations = settings.enable_rotation;
|
||||
params.nozzle_height = print_config.nozzle_height.value;
|
||||
params.all_objects_are_short = print.is_all_objects_are_short();
|
||||
params.align_center = print_config.best_object_pos.value;
|
||||
params.allow_multi_materials_on_same_plate = settings.allow_multi_materials_on_same_plate;
|
||||
params.avoid_extrusion_cali_region = settings.avoid_extrusion_cali_region;
|
||||
|
|
|
|||
|
|
@ -142,8 +142,9 @@ void CreateFontImageJob::finalize(bool canceled, std::exception_ptr &)
|
|||
glsafe(::glBindTexture(target, m_input.texture_id));
|
||||
|
||||
GLsizei w = m_tex_size.x(), h = m_tex_size.y();
|
||||
GLint xoffset = m_input.size.x() - m_tex_size.x(), // arrange right
|
||||
yoffset = m_input.size.y() * m_input.index;
|
||||
GLint xoffset = 0; // align to left
|
||||
// GLint xoffset = m_input.size.x() - m_tex_size.x(); // align right
|
||||
GLint yoffset = m_input.size.y() * m_input.index;
|
||||
glsafe(::glTexSubImage2D(target, m_input.level, xoffset, yoffset, w, h,
|
||||
m_input.format, m_input.type, m_result.data()));
|
||||
|
||||
|
|
|
|||
|
|
@ -42,12 +42,12 @@ void CreateFontStyleImagesJob::process(Ctl &ctl)
|
|||
// create image description
|
||||
StyleManager::StyleImage &image = m_images[index];
|
||||
BoundingBox &bounding_box = image.bounding_box;
|
||||
for (ExPolygon &shape : shapes)
|
||||
for (const ExPolygon &shape : shapes)
|
||||
bounding_box.merge(BoundingBox(shape.contour.points));
|
||||
for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min);
|
||||
|
||||
// calculate conversion from FontPoint to screen pixels by size of font
|
||||
double scale = get_text_shape_scale(item.prop, *item.font.font_file);
|
||||
double scale = get_text_shape_scale(item.prop, *item.font.font_file) * m_input.ppm;
|
||||
scales[index] = scale;
|
||||
|
||||
//double scale = font_prop.size_in_mm * SCALING_FACTOR;
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ void FillBedJob::prepare()
|
|||
|
||||
ArrangePolygon ap = get_instance_arrange_poly(mo->instances[inst_idx], global_config);
|
||||
BoundingBox ap_bb = ap.transformed_poly().contour.bounding_box();
|
||||
ap.height = 1;
|
||||
ap.name = mo->name;
|
||||
|
||||
if (selected)
|
||||
|
|
@ -173,7 +172,6 @@ void FillBedJob::prepare()
|
|||
ArrangePolygon ap = template_ap;
|
||||
ap.poly = m_selected.front().poly;
|
||||
ap.bed_idx = PartPlateList::MAX_PLATES_COUNT;
|
||||
ap.height = 1;
|
||||
ap.itemid = -1;
|
||||
ap.setter = [this, mi](const ArrangePolygon &p) {
|
||||
ModelObject *mo = m_plater->model().objects[m_object_idx];
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@
|
|||
#ifdef _WIN32
|
||||
#include <dbt.h>
|
||||
#include <shlobj.h>
|
||||
#include <shellapi.h>
|
||||
#endif // _WIN32
|
||||
#include <slic3r/GUI/CreatePresetsDialog.hpp>
|
||||
|
||||
|
|
@ -634,8 +635,64 @@ void MainFrame::bind_diff_dialog()
|
|||
|
||||
#ifdef __WIN32__
|
||||
|
||||
// Orca: Fix maximized window overlaps taskbar when taskbar auto hide is enabled (#8085)
|
||||
// Adopted from https://gist.github.com/MortenChristiansen/6463580
|
||||
static void AdjustWorkingAreaForAutoHide(const HWND hWnd, MINMAXINFO* mmi)
|
||||
{
|
||||
const auto taskbarHwnd = FindWindowA("Shell_TrayWnd", nullptr);
|
||||
if (!taskbarHwnd) {
|
||||
return;
|
||||
}
|
||||
const auto monitorContainingApplication = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONULL);
|
||||
const auto monitorWithTaskbarOnIt = MonitorFromWindow(taskbarHwnd, MONITOR_DEFAULTTONULL);
|
||||
if (monitorContainingApplication != monitorWithTaskbarOnIt) {
|
||||
return;
|
||||
}
|
||||
APPBARDATA abd;
|
||||
abd.cbSize = sizeof(APPBARDATA);
|
||||
abd.hWnd = taskbarHwnd;
|
||||
|
||||
// Find if task bar has auto-hide enabled
|
||||
const auto uState = (UINT) SHAppBarMessage(ABM_GETSTATE, &abd);
|
||||
if ((uState & ABS_AUTOHIDE) != ABS_AUTOHIDE) {
|
||||
return;
|
||||
}
|
||||
|
||||
RECT borderThickness;
|
||||
SetRectEmpty(&borderThickness);
|
||||
AdjustWindowRectEx(&borderThickness, GetWindowLongPtr(hWnd, GWL_STYLE) & ~WS_CAPTION, FALSE, 0);
|
||||
|
||||
// Determine taskbar position
|
||||
SHAppBarMessage(ABM_GETTASKBARPOS, &abd);
|
||||
const auto& rc = abd.rc;
|
||||
if (rc.top == rc.left && rc.bottom > rc.right) {
|
||||
// Left
|
||||
const auto offset = borderThickness.left + 2;
|
||||
mmi->ptMaxPosition.x += offset;
|
||||
mmi->ptMaxTrackSize.x -= offset;
|
||||
mmi->ptMaxSize.x -= offset;
|
||||
} else if (rc.top == rc.left && rc.bottom < rc.right) {
|
||||
// Top
|
||||
const auto offset = borderThickness.top + 2;
|
||||
mmi->ptMaxPosition.y += offset;
|
||||
mmi->ptMaxTrackSize.y -= offset;
|
||||
mmi->ptMaxSize.y -= offset;
|
||||
} else if (rc.top > rc.left) {
|
||||
// Bottom
|
||||
const auto offset = borderThickness.bottom + 2;
|
||||
mmi->ptMaxSize.y -= offset;
|
||||
mmi->ptMaxTrackSize.y -= offset;
|
||||
} else {
|
||||
// Right
|
||||
const auto offset = borderThickness.right + 2;
|
||||
mmi->ptMaxSize.x -= offset;
|
||||
mmi->ptMaxTrackSize.x -= offset;
|
||||
}
|
||||
}
|
||||
|
||||
WXLRESULT MainFrame::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam)
|
||||
{
|
||||
HWND hWnd = GetHandle();
|
||||
/* When we have a custom titlebar in the window, we don't need the non-client area of a normal window
|
||||
* to be painted. In order to achieve this, we handle the "WM_NCCALCSIZE" which is responsible for the
|
||||
* size of non-client area of a window and set the return value to 0. Also we have to tell the
|
||||
|
|
@ -654,7 +711,6 @@ WXLRESULT MainFrame::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam
|
|||
its wParam value is TRUE and the return value is 0 */
|
||||
case WM_NCCALCSIZE:
|
||||
if (wParam) {
|
||||
HWND hWnd = GetHandle();
|
||||
/* Detect whether window is maximized or not. We don't need to change the resize border when win is
|
||||
* maximized because all resize borders are gone automatically */
|
||||
WINDOWPLACEMENT wPos;
|
||||
|
|
@ -677,6 +733,13 @@ WXLRESULT MainFrame::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam
|
|||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_GETMINMAXINFO: {
|
||||
auto mmi = (MINMAXINFO*) lParam;
|
||||
HandleGetMinMaxInfo(mmi);
|
||||
AdjustWorkingAreaForAutoHide(hWnd, mmi);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return wxFrame::MSWWindowProc(nMsg, wParam, lParam);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1348,8 +1348,7 @@ void PartPlate::register_raycasters_for_picking(GLCanvas3D &canvas)
|
|||
|
||||
int PartPlate::picking_id_component(int idx) const
|
||||
{
|
||||
unsigned int id = PLATE_BASE_ID - this->m_plate_index * GRABBER_COUNT - idx;
|
||||
return id;
|
||||
return this->m_plate_index * GRABBER_COUNT + idx;
|
||||
}
|
||||
|
||||
std::vector<int> PartPlate::get_extruders(bool conside_custom_gcode) const
|
||||
|
|
|
|||
|
|
@ -198,7 +198,6 @@ private:
|
|||
int picking_id_component(int idx) const;
|
||||
|
||||
public:
|
||||
static const unsigned int PLATE_BASE_ID = 255 * 255 * 253;
|
||||
static const unsigned int PLATE_NAME_HOVER_ID = 6;
|
||||
static const unsigned int GRABBER_COUNT = 8;
|
||||
|
||||
|
|
|
|||
|
|
@ -4396,7 +4396,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
|
||||
cur_plate->translate_all_instance(new_origin - cur_origin);
|
||||
}
|
||||
view3D->get_canvas3d()->remove_raycasters_for_picking(SceneRaycaster::EType::Bed);
|
||||
partplate_list.reset_size(current_width, current_depth, current_height, true, true);
|
||||
partplate_list.register_raycasters_for_picking(*view3D->get_canvas3d());
|
||||
}
|
||||
|
||||
//BBS: add gcode loading logic in the end
|
||||
|
|
@ -5972,6 +5974,11 @@ void Plater::priv::reload_from_disk()
|
|||
}
|
||||
}
|
||||
if (found) break;
|
||||
// BBS: step model,object loaded as a volume. GUI_ObfectList.cpp load_modifier()
|
||||
if (obj->name == old_volume->name) {
|
||||
new_object_idx = (int) o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5980,22 +5987,30 @@ void Plater::priv::reload_from_disk()
|
|||
continue;
|
||||
}
|
||||
ModelObject *new_model_object = new_model.objects[new_object_idx];
|
||||
if (new_volume_idx < 0 || int(new_model_object->volumes.size()) <= new_volume_idx) {
|
||||
if (int(new_model_object->volumes.size()) <= new_volume_idx) {
|
||||
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
|
||||
continue;
|
||||
}
|
||||
|
||||
old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]);
|
||||
ModelVolume *new_volume = old_model_object->volumes.back();
|
||||
ModelVolume *new_volume = nullptr;
|
||||
// BBS: step model
|
||||
if (new_volume_idx < 0 && new_object_idx >= 0) {
|
||||
TriangleMesh mesh = new_model_object->mesh();
|
||||
new_volume = old_model_object->add_volume(std::move(mesh));
|
||||
new_volume->name = new_model_object->name;
|
||||
new_volume->source.input_file = new_model_object->input_file;
|
||||
}else {
|
||||
new_volume = old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]);
|
||||
// new_volume = old_model_object->volumes.back();
|
||||
}
|
||||
|
||||
new_volume->set_new_unique_id();
|
||||
new_volume->config.apply(old_volume->config);
|
||||
new_volume->set_type(old_volume->type());
|
||||
new_volume->set_material_id(old_volume->material_id());
|
||||
|
||||
Transform3d transform = Transform3d::Identity();
|
||||
transform.translate(new_volume->source.mesh_offset - old_volume->source.mesh_offset);
|
||||
new_volume->set_transformation(old_volume->get_transformation().get_matrix() * old_volume->source.transform.get_matrix_no_offset() *
|
||||
transform * new_volume->source.transform.get_matrix_no_offset().inverse());
|
||||
new_volume->source.mesh_offset = old_volume->source.mesh_offset;
|
||||
new_volume->set_transformation(old_volume->get_transformation());
|
||||
|
||||
new_volume->source.object_idx = old_volume->source.object_idx;
|
||||
new_volume->source.volume_idx = old_volume->source.volume_idx;
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public:
|
|||
enum class EIdBase
|
||||
{
|
||||
Bed = 0,
|
||||
Volume = 1000,
|
||||
Volume = 1000, // Must be smaller than PartPlateList::MAX_PLATES_COUNT * PartPlate::GRABBER_COUNT
|
||||
Gizmo = 1000000,
|
||||
FallbackGizmo = 2000000
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2636,14 +2636,14 @@ void TabPrintModel::update_model_config()
|
|||
}
|
||||
else {
|
||||
replace(m_all_keys.begin(), m_all_keys.end(), std::string("first_layer_print_sequence"), std::string("first_layer_sequence_choice"));
|
||||
m_config->set_key_value("first_layer_sequence_choice", new ConfigOptionEnum<LayerSeq>(flsCutomize));
|
||||
m_config->set_key_value("first_layer_sequence_choice", new ConfigOptionEnum<LayerSeq>(flsCustomize));
|
||||
}
|
||||
if (!plate_config.has("other_layers_print_sequence")) {
|
||||
m_config->set_key_value("other_layers_sequence_choice", new ConfigOptionEnum<LayerSeq>(flsAuto));
|
||||
}
|
||||
else {
|
||||
replace(m_all_keys.begin(), m_all_keys.end(), std::string("other_layers_print_sequence"), std::string("other_layers_sequence_choice"));
|
||||
m_config->set_key_value("other_layers_sequence_choice", new ConfigOptionEnum<LayerSeq>(flsCutomize));
|
||||
m_config->set_key_value("other_layers_sequence_choice", new ConfigOptionEnum<LayerSeq>(flsCustomize));
|
||||
}
|
||||
notify_changed(plate_item.first);
|
||||
}
|
||||
|
|
@ -2883,7 +2883,7 @@ void TabPrintPlate::on_value_change(const std::string& opt_key, const boost::any
|
|||
if (first_layer_seq_choice == LayerSeq::flsAuto) {
|
||||
plate->set_first_layer_print_sequence({});
|
||||
}
|
||||
else if (first_layer_seq_choice == LayerSeq::flsCutomize) {
|
||||
else if (first_layer_seq_choice == LayerSeq::flsCustomize) {
|
||||
const DynamicPrintConfig& plate_config = plate_item.second->get();
|
||||
if (!plate_config.has("first_layer_print_sequence")) {
|
||||
std::vector<int> initial_sequence;
|
||||
|
|
@ -2904,7 +2904,7 @@ void TabPrintPlate::on_value_change(const std::string& opt_key, const boost::any
|
|||
if (other_layer_seq_choice == LayerSeq::flsAuto) {
|
||||
plate->set_other_layers_print_sequence({});
|
||||
}
|
||||
else if (other_layer_seq_choice == LayerSeq::flsCutomize) {
|
||||
else if (other_layer_seq_choice == LayerSeq::flsCustomize) {
|
||||
const DynamicPrintConfig& plate_config = plate_item.second->get();
|
||||
if (!plate_config.has("other_layers_print_sequence")) {
|
||||
std::vector<int> initial_sequence;
|
||||
|
|
@ -5115,13 +5115,12 @@ bool Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
|
|||
apply_config_from_cache();
|
||||
|
||||
// Orca: update presets for the selected printer
|
||||
load_current_preset();
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
load_current_preset();
|
||||
|
||||
|
||||
if (delete_third_printer) {
|
||||
wxGetApp().CallAfter([filament_presets, process_presets]() {
|
||||
|
|
|
|||
|
|
@ -24,12 +24,14 @@ static const std::string temp_gcode_path = temp_dir + "/temp.gcode";
|
|||
static const std::string path = temp_dir + "/test.3mf";
|
||||
static const std::string config_3mf_path = temp_dir + "/test_config.3mf";
|
||||
|
||||
static std::string MachineBedTypeString[5] = {
|
||||
static std::string MachineBedTypeString[7] = {
|
||||
"auto",
|
||||
"suprtack",
|
||||
"pc",
|
||||
"ep",
|
||||
"pei",
|
||||
"pte"
|
||||
"pte",
|
||||
"pct",
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue