Merge branch 'main' into pr/tommasobbianchi/15238

This commit is contained in:
SoftFever
2026-09-08 14:28:31 +08:00
4759 changed files with 44361 additions and 18139 deletions
+1
View File
@@ -1460,6 +1460,7 @@ void AppConfig::set_mouse_device(const std::string& name, double translation_spe
it->second["invert_yaw"] = invert_yaw ? "1" : "0";
it->second["invert_pitch"] = invert_pitch ? "1" : "0";
it->second["invert_roll"] = invert_roll ? "1" : "0";
m_dirty = true;
}
std::vector<std::string> AppConfig::get_mouse_device_names() const
+1 -1
View File
@@ -797,7 +797,7 @@ public:
}
});
m_pck.unfitIndicator([this](std::string name) {
m_pck.unfitIndicator([](std::string name) {
BOOST_LOG_TRIVIAL(debug) << "arrange progress: " + name;
});
@@ -1,6 +1,7 @@
#include "BlacklistedLibraryCheck.hpp"
#include <cstdio>
#include <boost/filesystem/path.hpp>
#include <boost/nowide/convert.hpp>
#ifdef WIN32
+7 -3
View File
@@ -13,7 +13,6 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
: m_bed_shape(printable_area), m_max_print_height(printable_height), m_extruder_shapes(extruder_areas), m_extruder_printable_height(extruder_printable_heights)
{
assert(printable_height >= 0);
//assert(extruder_printable_heights.size() == extruder_areas.size());
m_polygon = Polygon::new_scale(printable_area);
assert(m_polygon.is_counter_clockwise());
@@ -86,6 +85,9 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
m_shared_volume.data[2] = m_bboxf.max.x();
m_shared_volume.data[3] = m_bboxf.max.y();
m_shared_volume.zs[1] = m_bboxf.max.z();
if (extruder_printable_heights.size() < m_extruder_shapes.size())
BOOST_LOG_TRIVIAL(warning) << boost::format("extruder_printable_height has only %1% entries but extruder_printable_area has %2%, falling back to the bed printable_height for the missing ones")
% extruder_printable_heights.size() % m_extruder_shapes.size();
for (unsigned int index = 0; index < m_extruder_shapes.size(); index++)
{
std::vector<Vec2d>& extruder_shape = m_extruder_shapes[index];
@@ -100,7 +102,9 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
return;
}
if ((extruder_shape == printable_area)&&(extruder_printable_heights[index] == printable_height)) {
const double extruder_height = index < extruder_printable_heights.size() ? extruder_printable_heights[index] : printable_height;
if ((extruder_shape == printable_area)&&(extruder_height == printable_height)) {
extruder_volume.same_with_bed = true;
extruder_volume.type = m_type;
extruder_volume.bbox = m_bbox;
@@ -113,7 +117,7 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
double poly_area = poly.area();
extruder_volume.bbox = get_extents(poly);
BoundingBoxf temp_bboxf = get_extents(extruder_shape);
extruder_volume.bboxf = BoundingBoxf3{ to_3d(temp_bboxf.min, 0.), to_3d(temp_bboxf.max, extruder_printable_heights[index]) };
extruder_volume.bboxf = BoundingBoxf3{ to_3d(temp_bboxf.min, 0.), to_3d(temp_bboxf.max, extruder_height) };
if (extruder_shape.size() >= 4 && std::abs((poly_area - double(extruder_volume.bbox.size().x()) * double(extruder_volume.bbox.size().y()))) < sqr(SCALED_EPSILON))
{
+9
View File
@@ -152,6 +152,8 @@ set(lisbslic3r_sources
Fill/FillBase.hpp
Fill/FillConcentric.cpp
Fill/FillConcentric.hpp
Fill/FillSpiralInset.cpp
Fill/FillSpiralInset.hpp
Fill/FillConcentricInternal.cpp
Fill/FillConcentricInternal.hpp
Fill/FillCornerSmoothing.cpp
@@ -719,6 +721,13 @@ if(SLIC3R_PROFILE)
target_link_libraries(libslic3r PRIVATE Shiny)
endif()
if (WIN32)
# Public, since BlacklistedLibraryCheck.hpp includes windows.h. Empty
# WIN32_LEAN_AND_MEAN matches the sources that define it themselves; bare
# NOMINMAX matches the one libigl already passes.
target_compile_definitions(libslic3r PUBLIC "WIN32_LEAN_AND_MEAN=" "NOMINMAX")
endif ()
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
add_precompiled_header(libslic3r pchheader.hpp FORCEINCLUDE)
endif ()
+51 -5
View File
@@ -1049,7 +1049,8 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
std::vector<std::string>& different_settings = this->option<ConfigOptionStrings>("different_settings_to_system", true)->values;
size_t size = different_settings.size();
if (size == 0) {
size = this->option<ConfigOptionStrings>("filament_settings_id")->values.size() + 2;
const auto *filament_ids = this->option<ConfigOptionStrings>("filament_settings_id");
size = (filament_ids ? filament_ids->values.size() : 0) + 2;
different_settings.resize(size);
}
@@ -1715,6 +1716,36 @@ const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key) co
return (it == options.end()) ? nullptr : it->second.get();
}
// ConfigOptionBool(s)::deserialize only understands "1" and "0", but scripts commonly spell CLI
// flags as --opt=true or --opt=no. Map the usual spellings onto what deserialize() accepts, per
// comma-separated item so vector options keep working, and pass anything else through unchanged
// so a genuine typo is still reported as invalid.
static std::string normalize_cli_bool_value(const std::string &value)
{
static const char* true_values[] = { "1", "true", "yes", "on", "enabled" };
static const char* false_values[] = { "0", "false", "no", "off", "disabled" };
auto matches = [](const std::string &item, const char* const* candidates, size_t count) {
return std::any_of(candidates, candidates + count, [&item](const char* candidate) { return boost::iequals(item, candidate); });
};
std::string normalized;
std::istringstream is(value);
std::string item;
while (std::getline(is, item, ',')) {
boost::trim(item);
if (! normalized.empty())
normalized += ",";
if (matches(item, true_values, std::size(true_values)))
normalized += "1";
else if (matches(item, false_values, std::size(false_values)))
normalized += "0";
else
normalized += item;
}
return normalized;
}
bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys)
{
// cache the CLI option => opt_key mapping
@@ -1812,17 +1843,32 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option
// to the end of the value.
if (opt_base->type() == coBools && value.empty())
static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no);
else
else {
// Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way
// they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape
// will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be
// unescaped by the calling shell.
opt_vector->deserialize(value, true);
const std::string vector_value = opt_base->type() == coBools ? normalize_cli_bool_value(value) : value;
bool deserialized = false;
try {
deserialized = opt_vector->deserialize(vector_value, true);
} catch (const std::exception &ex) {
// e.g. "nil" deserialized into a non-nullable vector option throws instead of
// returning false - treat that the same as any other invalid value here.
deserialized = false;
}
if (! deserialized) {
boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
return false;
}
}
} else if (opt_base->type() == coBool) {
if (value.empty())
static_cast<ConfigOptionBool*>(opt_base)->value = !no;
else
opt_base->deserialize(value);
else if (! opt_base->deserialize(normalize_cli_bool_value(value))) {
boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
return false;
}
} else if (opt_base->type() == coString) {
// Do not unescape single string values, the unescaping is left to the calling shell.
static_cast<ConfigOptionString*>(opt_base)->value = value;
+4 -2
View File
@@ -950,7 +950,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
params.extruder = region_config.internal_solid_filament_id;
// Orca: forced fill order applies only to top/bottom surfaces filled with a
// center-based pattern; everything else stays at Default to keep batching together.
if (params.pattern == ipConcentric || params.pattern == ipArchimedeanChords || params.pattern == ipOctagramSpiral) {
if (params.pattern == ipConcentric || params.pattern == ipSpiralInset || params.pattern == ipArchimedeanChords || params.pattern == ipOctagramSpiral) {
if (params.extrusion_role == erTopSolidInfill)
params.fill_order = region_config.top_surface_fill_order.value;
else if (params.extrusion_role == erBottomSurface)
@@ -1332,7 +1332,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.anchor_length = surface_fill.params.anchor_length;
params.anchor_length_max = surface_fill.params.anchor_length_max;
params.resolution = resolution;
params.use_arachne = surface_fill.params.pattern == ipConcentric || surface_fill.params.pattern == ipConcentricInternal;
params.use_arachne = surface_fill.params.pattern == ipConcentric || surface_fill.params.pattern == ipSpiralInset ||
surface_fill.params.pattern == ipConcentricInternal;
params.layer_height = layerm->layer()->height;
params.lateral_lattice_angle_1 = surface_fill.params.lateral_lattice_angle_1;
params.lateral_lattice_angle_2 = surface_fill.params.lateral_lattice_angle_2;
@@ -1515,6 +1516,7 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
case ipCubic:
case ipLine:
case ipConcentric:
case ipSpiralInset:
case ipHoneycomb:
case ipLateralHoneycomb:
case ip3DHoneycomb:
+7 -3
View File
@@ -15,6 +15,7 @@
#include "FillBase.hpp"
#include "FillConcentric.hpp"
#include "FillSpiralInset.hpp"
#include "FillHoneycomb.hpp"
#include "Fill3DHoneycomb.hpp"
#include "FillGyroid.hpp"
@@ -41,6 +42,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
{
switch (type) {
case ipConcentric: return new FillConcentric();
case ipSpiralInset: return new FillSpiralInset();
case ipHoneycomb: return new FillHoneycomb();
case ipLateralHoneycomb: return new FillLateralHoneycomb();
case ip3DHoneycomb: return new Fill3DHoneycomb();
@@ -2465,9 +2467,11 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector<co
#endif // INFILL_DEBUG_OUTPUT
const std::vector<SupportArcCost> arches = evaluate_support_arches(infill_ordered, graph, spacing, params);
static const double cost_low = line_spacing * 1.3;
static const double cost_high = line_spacing * 2.;
static const double cost_veryhigh = line_spacing * 3.;
// Must not be static: line_spacing varies per call (base vs interface fills differ),
// and a static here would fix these to whichever call ran first, order depending on thread count.
const double cost_low = line_spacing * 1.3;
const double cost_high = line_spacing * 2.;
const double cost_veryhigh = line_spacing * 3.;
{
std::vector<const SupportArcCost*> selected;
+1 -6
View File
@@ -2395,12 +2395,7 @@ static std::vector<MonotonicRegionLink> chain_monotonic_regions(
}
// Probability (unnormalized) of traversing a link between two monotonic regions.
auto path_probability = [
#ifndef __APPLE__
// clang complains when capturing constexpr constants.
pheromone_alpha, pheromone_beta
#endif // __APPLE__
](AntPath &path) {
auto path_probability = [](AntPath &path) {
return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta);
};
+426
View File
@@ -0,0 +1,426 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "../VariableWidth.hpp"
#include "Arachne/WallToolPaths.hpp"
#include "FillSpiralInset.hpp"
#include <algorithm>
#include <cmath>
#include <functional>
namespace Slic3r {
// Index of the corner the spiral should start at. Every following loop is split at the point nearest
// the end of the one before it, so this choice propagates inwards and decides where the whole spiral
// hands over from ring to ring. A tight corner is the worst place for it: there the next ring
// retreats along the bisector by spacing/sin(angle), so the spiral has to strike out several spacings
// to reach it instead of stepping across to a ring running parallel one spacing away.
//
// A right angle is taken first when the loop has one. It clips cleanly, since the trimming below
// scales with 1/sin(angle) and so is at its shortest and least sensitive there, and it holds its
// shape as the loop is offset inwards, which keeps the handover in the same place ring after ring.
// Failing that the widest corner is the flattest stretch on offer, which is the next best handover.
// A straight point is no corner at all and only turns up as an artefact of the offsetting, so it is
// skipped.
static int find_spiral_start_corner(const Polygon& loop)
{
const size_t n = loop.points.size();
if (n < 3)
return 0;
// cos(85 deg): a corner within five degrees of square counts as a right angle.
static const double right_angle_cos = 0.08716;
// cos(179 deg): anything flatter than this counts as a straight point rather than a corner.
static const double straight_cos = -0.99985;
// Only convex corners qualify. A reflex corner spans the same angle between its two edges but
// bulges the other way, so the next ring in steps away from it along the bisector instead of
// hugging it, and starting there hands over across a long diagonal on every single ring. Loops
// arrive counter-clockwise, in which case a convex corner turns left, but check the winding
// rather than trust it. A closed loop always has at least one convex corner.
const double convex_turn = loop.is_counter_clockwise() ? 1.0 : -1.0;
double best_right_cos = right_angle_cos;
int best_right = -1;
double best_wide_cos = 1.0;
int best_wide = -1;
for (size_t i = 0; i < n; ++i) {
const Point& p_prev = loop.points[(i - 1 + n) % n];
const Point& p = loop.points[i];
const Point& p_next = loop.points[(i + 1) % n];
Vec2d e_in = (p - p_prev).cast<double>();
Vec2d e_out = (p_next - p).cast<double>();
double len1 = e_in.norm();
double len2 = e_out.norm();
if (len1 < 1e-6 || len2 < 1e-6)
continue;
if (convex_turn * (e_in.x() * e_out.y() - e_in.y() * e_out.x()) <= 0.0)
continue;
// Cosine of the angle the two edges span at the corner: 1 at a spike, 0 square, -1 straight.
double cos_val = -e_in.dot(e_out) / (len1 * len2);
if (std::abs(cos_val) < best_right_cos) {
best_right_cos = std::abs(cos_val);
best_right = int(i);
}
if (cos_val > straight_cos && cos_val < best_wide_cos) {
best_wide_cos = cos_val;
best_wide = int(i);
}
}
if (best_right >= 0)
return best_right;
// A loop smooth enough to have no corner at all, a circle say, hands over equally well anywhere.
return best_wide < 0 ? 0 : best_wide;
}
// Length to trim off the end of a loop so that it does not overlap the start of the next one.
// The theoretical gap is distance/sin(alpha), alpha being the angle between the last segment of the
// loop and the first segment of the next one.
static double loop_clip_length(const Polyline& loop_path, const double gap)
{
const Point& p_prev = loop_path.points[loop_path.points.size() - 2];
const Point& p_last = loop_path.points.back();
const Point& p_next = loop_path.points[1];
Vec2d v1 = (p_last - p_prev).cast<double>();
Vec2d v2 = (p_next - p_last).cast<double>();
if (v1.norm() < 1e-6 || v2.norm() < 1e-6)
return gap;
double alpha = std::atan2(std::abs(v1.x() * v2.y() - v1.y() * v2.x()), v1.dot(v2));
// Outside 45deg < alpha < 120deg the 1/sin(alpha) term would clip far too much, so fall back to the plain gap.
return (alpha > M_PI / 4 && alpha < 2 * M_PI / 3) ? gap / std::sin(alpha) : gap;
}
// The chaining below drives two kinds of loop: the plain offset polygons of the classic path, and
// Arachne's variable width walls. These are the only four steps that differ between them. Widths run
// two per segment, so every point added or removed takes a pair with it.
static Polyline open_loop(const Polygon& loop, int start_index) { return loop.split_at_index(start_index); }
static ThickPolyline open_loop(const Arachne::ExtrusionLine& loop, int start_index)
{
ThickPolyline path = Arachne::to_thick_polyline(loop);
// start_at_index() rotates a closed path, and wants it closed with a matching width at both ends.
if (path.points.front() != path.points.back()) {
const coordf_t w_first = path.width.front(), w_last = path.width.back();
path.points.emplace_back(path.points.front());
path.width.emplace_back(w_last);
path.width.emplace_back(w_first);
}
path.start_at_index(start_index);
return path;
}
static void clip_path_end(Polyline& path, double distance) { path.clip_end(distance); }
static void clip_path_end(ThickPolyline& path, double distance)
{
// Polyline::clip_end() knows nothing about the widths, so walk back trimming the two together.
while (distance > 0 && path.points.size() >= 2) {
const Point last = path.points.back();
const coordf_t w_end = path.width.back();
path.points.pop_back();
path.width.pop_back();
const coordf_t w_start = path.width.back();
path.width.pop_back();
const Vec2d v = (path.points.back() - last).cast<double>();
const double len = v.norm();
if (len > distance) {
const double t = distance / len;
path.points.emplace_back((last.cast<double>() + v * t).cast<coord_t>());
path.width.emplace_back(w_start);
path.width.emplace_back(w_start + (w_end - w_start) * (1.0 - t));
return;
}
distance -= len;
}
path.clear();
}
static void append_path(Polyline& dst, Polyline&& src) { dst.append(std::move(src)); }
static void append_path(ThickPolyline& dst, ThickPolyline&& src)
{
if (dst.empty()) {
dst = std::move(src);
return;
}
if (dst.points.back() == src.points.front()) {
// Carrying straight on from the same point, so there is no run across to give a width to.
src.points.erase(src.points.begin());
src.width.erase(src.width.begin(), src.width.begin() + 2);
} else {
// The run across to the next loop tapers between the two ends it joins.
const coordf_t w_from = dst.width.back(), w_to = src.width.front();
dst.width.emplace_back(w_from);
dst.width.emplace_back(w_to);
}
append(dst.points, std::move(src.points));
append(dst.width, std::move(src.width));
}
// The classic loops all carry the same width, so the innermost one of an island can still ring an
// unfilled pin hole, which the spiral plugs by running into the middle. Arachne's walls widen to take
// up whatever is left over, so there is nothing there to plug and the stub would only double back
// over the wall that just filled it.
static bool leaves_a_centre_hole(const Polygon&) { return true; }
static bool leaves_a_centre_hole(const Arachne::ExtrusionLine&) { return false; }
static void append_path_point(Polyline& path, const Point& point) { path.points.emplace_back(point); }
static void append_path_point(ThickPolyline& path, const Point& point)
{
const coordf_t w = path.width.back();
path.points.emplace_back(point);
path.width.emplace_back(w);
path.width.emplace_back(w);
}
// Chain the loops of one surface into as few continuous spirals as its shape allows. The loops arrive
// ordered outside in, depth first, each paired with its outline in loop_outlines; every decision here
// is made on those outlines, so the two kinds of loop take exactly the same route.
template<class LoopType, class PathType>
static std::vector<PathType> generate_spiral_insets(const FillParams& params,
const std::vector<const LoopType*>& loops,
const Polygons& loop_outlines,
const coord_t distance,
const ExPolygon& original_expoly)
{
std::vector<PathType> output;
PathType spiral;
Point current_pos(0, 0);
// Index into loops of the innermost loop appended to the spiral currently being built.
int innermost_loop = -1;
// Whether the spiral can run straight from one point to the other. The run across is extruded,
// not travelled, so it has to be a genuine step over to the ring alongside:
// - up to a ring spacing and a half it cannot leave the material, and needs no check at all,
// which covers all but a few of the loops;
// - beyond that it is tested against the surface, which catches the points that are close in a
// straight line but separated by a hole or a notch;
// - past four spacings it is refused outright. A handover does stretch at a corner, where the
// next ring retreats along the bisector by spacing/sin(angle), but four spacings is already a
// fifteen degree wedge, and down a wedge that tight the run across would trace the bisector,
// which is where the tail is filled from anyway. Anything longer is a traverse across the
// surface that prints over what it crosses. Breaking the spiral leaves the G-code to travel it.
const double free_hop = 1.5 * double(distance);
const double max_hop = 4.0 * double(distance);
auto reachable = [&](const Point& from, const Point& to) {
const double hop = from.distance_to(to);
if (hop > max_hop)
return false;
return hop <= free_hop || original_expoly.contains(Line(from, to));
};
// The centre point plugs the pin hole left in the middle of an island, it is not meant to
// traverse it, so it is only worth adding when the innermost loop has shrunk to about a ring.
const double max_center_stub = 2.0 * double(distance);
// Emit the spiral built so far as one path and start over on a fresh island.
auto flush_spiral = [&]() {
if (spiral.empty())
return;
// Run into the middle of the innermost loop so the island's centre is filled instead of being
// left as a pin hole. Only where there is a hole to fill: the loop has to still enclose open
// space once its own bead is accounted for, or the stub just runs back over that bead. And
// the point has to sit inside the loop and be reachable, or it runs off across the surface.
if (innermost_loop >= 0 && leaves_a_centre_hole(*loops[innermost_loop])) {
const Polygon& innermost = loop_outlines[innermost_loop];
const Point centroid = innermost.centroid();
if (!offset(innermost, -float(0.5 * double(distance))).empty() && centroid != spiral.last_point() &&
spiral.last_point().distance_to(centroid) <= max_center_stub && innermost.contains(centroid) &&
reachable(spiral.last_point(), centroid))
append_path_point(spiral, centroid);
}
output.emplace_back(std::move(spiral));
spiral.clear();
innermost_loop = -1;
current_pos = Point(0, 0);
};
for (size_t i = 0; i < loops.size(); ++i) {
const Polygon& outline = loop_outlines[i];
if (outline.points.empty())
continue;
// The loop is opened into a path with the split point repeated at both ends, so a usable one
// has at least 3 points. Both kinds of loop share the outline's indices, hence its start point.
PathType loop_path = open_loop(*loops[i], spiral.empty() ? find_spiral_start_corner(outline) :
current_pos.nearest_point_index(outline.points));
if (loop_path.size() < 3)
continue;
// Island jumping: the loops are ordered by their nesting, depth first, so the next one
// continues the current spiral exactly when it lies inside the one just laid down. Distance
// cannot stand in for that test: at a sharp corner the next ring retreats along the bisector
// by spacing/sin(angle), which leaves it several spacings away while still being the very
// next ring in, and the spiral would break off at every spike.
const bool same_island = innermost_loop >= 0 && loop_outlines[innermost_loop].contains(loop_path.points.front());
if (!spiral.empty() && (!same_island || !reachable(spiral.last_point(), loop_path.points.front()))) {
flush_spiral();
loop_path = open_loop(*loops[i], find_spiral_start_corner(outline));
if (loop_path.size() < 3)
continue;
}
// Clip the end of the loop to leave room for the run into the next one. The last loop of the
// surface has no successor, so it only gives up half of the gap.
clip_path_end(loop_path, loop_clip_length(loop_path, (i + 1 == loops.size() ? 0.5 : 1.0) * double(distance)));
// Clipping empties the path when the loop is shorter than the clipping length, which happens
// on the degenerate slivers that offsetting leaves behind. Such a loop carries no extrusion.
if (loop_path.size() < 2)
continue;
append_path(spiral, std::move(loop_path));
innermost_loop = int(i);
current_pos = spiral.last_point();
}
flush_spiral();
// An outward fill order runs every spiral from its centre to its outer edge, innermost island first.
if (params.fill_order != SurfaceFillOrder::Inward) {
for (PathType& path : output)
path.reverse();
std::reverse(output.begin(), output.end());
}
return output;
}
void FillSpiralInset::_fill_surface_single(const FillParams& params,
unsigned int thickness_layers,
const std::pair<float, Point>& direction,
ExPolygon expolygon,
Polylines& polylines_out)
{
BoundingBox bounding_box = expolygon.contour.bounding_box();
coord_t min_spacing = scale_(this->spacing);
coord_t distance = coord_t(min_spacing / params.density);
if (params.density > 0.9999f && !params.dont_adjust) {
distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance);
this->spacing = unscale<double>(distance);
}
Polygons loops = to_polygons(expolygon);
ExPolygons last{std::move(expolygon)};
while (!last.empty()) {
last = offset2_ex(last, -(distance + min_spacing / 2), +min_spacing / 2);
append(loops, to_polygons(last));
}
// Orders the loops outside in, depth first, which is the order the chaining below expects.
loops = union_pt_chained_outside_in(loops);
std::vector<const Polygon*> loop_refs;
loop_refs.reserve(loops.size());
for (const Polygon& loop : loops)
loop_refs.emplace_back(&loop);
Polylines spiral_result = generate_spiral_insets<Polygon, Polyline>(params, loop_refs, loops, distance, expolygon);
append(polylines_out, spiral_result);
}
void FillSpiralInset::_fill_surface_single(const FillParams& params,
unsigned int thickness_layers,
const std::pair<float, Point>& direction,
ExPolygon expolygon,
ThickPolylines& thick_polylines_out)
{
assert(params.use_arachne);
assert(this->print_config != nullptr && this->print_object_config != nullptr);
// Only a solid surface is worth the variable width walls; a sparse one falls back to plain loops.
if (params.density <= 0.9999f || params.dont_adjust) {
Polylines polylines;
this->_fill_surface_single(params, thickness_layers, direction, expolygon, polylines);
append(thick_polylines_out, to_thick_polylines(std::move(polylines), scaled<coord_t>(this->spacing)));
return;
}
// no rotation is supported for this infill pattern
Point bbox_size = expolygon.contour.bounding_box().size();
coord_t min_spacing = scaled<coord_t>(this->spacing);
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
Polygons polygons = offset(expolygon, float(min_spacing) / 2.f);
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
Arachne::WallToolPathsParams input_params;
input_params.min_bead_width = 0.85 * min_nozzle_diameter;
input_params.min_feature_size = 0.25 * min_nozzle_diameter;
input_params.wall_transition_length = 1.0 * min_nozzle_diameter;
input_params.wall_transition_angle = 10;
input_params.wall_transition_filter_deviation = 0.25 * min_nozzle_diameter;
input_params.wall_distribution_count = 1;
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, params.layer_height, input_params);
std::vector<Arachne::VariableWidthLines> walls_by_inset = wallToolPaths.getToolPaths();
// Open walls are the thin features Arachne fits between the closed ones. They cannot join a
// spiral, so they go out as they are; leaving them behind is what would put the gaps back.
std::vector<const Arachne::ExtrusionLine*> walls;
Polygons wall_outlines;
ThickPolylines open_walls;
for (const Arachne::VariableWidthLines& inset : walls_by_inset)
for (const Arachne::ExtrusionLine& wall : inset) {
if (wall.empty())
continue;
if (wall.is_closed) {
walls.emplace_back(&wall);
wall_outlines.emplace_back(wall.toPolygon());
} else {
open_walls.emplace_back(Arachne::to_thick_polyline(wall));
}
}
// Arachne hands the walls back grouped by inset, which is not their nesting: around a hole the
// wall of a given inset lies inside the wall of that same inset around the contour. Nest them by
// containment instead, so the spiral follows one island all the way in before starting the next,
// the same order union_pt_chained_outside_in gives the classic path above.
const size_t wall_count = walls.size();
std::vector<int> nesting_depth(wall_count, 0), parent(wall_count, -1);
std::vector<char> inside(wall_count * wall_count, 0);
for (size_t i = 0; i < wall_count; ++i)
for (size_t j = 0; j < wall_count; ++j)
if (i != j && wall_outlines[j].contains(walls[i]->junctions.front().p)) {
inside[i * wall_count + j] = 1;
++nesting_depth[i];
}
// The innermost of the walls containing this one, which is the deepest of them, is its parent.
for (size_t i = 0; i < wall_count; ++i)
for (size_t j = 0; j < wall_count; ++j)
if (inside[i * wall_count + j] && (parent[i] < 0 || nesting_depth[parent[i]] < nesting_depth[j]))
parent[i] = int(j);
std::vector<const Arachne::ExtrusionLine*> ordered;
Polygons outlines;
ordered.reserve(wall_count);
outlines.reserve(wall_count);
std::function<void(int)> descend = [&](int idx) {
ordered.emplace_back(walls[idx]);
outlines.emplace_back(wall_outlines[idx]);
for (size_t k = 0; k < wall_count; ++k)
if (parent[k] == idx)
descend(int(k));
};
for (size_t i = 0; i < wall_count; ++i)
if (parent[i] < 0)
descend(int(i));
ThickPolylines spiral_result =
generate_spiral_insets<Arachne::ExtrusionLine, ThickPolyline>(params, ordered, outlines, min_spacing, expolygon);
append(thick_polylines_out, std::move(spiral_result));
append(thick_polylines_out, std::move(open_walls));
}
} // namespace Slic3r
+37
View File
@@ -0,0 +1,37 @@
#ifndef slic3r_FillSpiralInset_hpp_
#define slic3r_FillSpiralInset_hpp_
#include "FillBase.hpp"
namespace Slic3r {
class FillSpiralInset : public Fill
{
public:
~FillSpiralInset() override = default;
bool is_self_crossing() override { return false; }
protected:
Fill* clone() const override { return new FillSpiralInset(*this); };
void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon expolygon,
Polylines &polylines_out) override;
// Orca: solid surfaces are filled with Arachne's variable width walls, which widen to take up
// whatever the fixed width loops above would have left over as gaps.
void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
const std::pair<float, Point> &direction,
ExPolygon expolygon,
ThickPolylines &thick_polylines_out) override;
bool no_sort() const override { return true; }
};
} // namespace Slic3r
#endif // slic3r_FillSpiralInset_hpp_
+1
View File
@@ -10,6 +10,7 @@
#include <string>
#include <boost/log/trivial.hpp>
#include <boost/nowide/fstream.hpp>
#ifdef _WIN32
#define DIR_SEPARATOR '\\'
+4 -3
View File
@@ -8197,9 +8197,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
}
variable_speed = std::any_of(new_points.begin(), new_points.end(),
[speed](const ProcessedPoint &p) { return fabs(double(p.speed) - speed) > 1; }); // Ignore small speed variations (under 1mm/sec)
if (!NOZZLE_CONFIG(enable_overhang_speed) && FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers) {
for (ProcessedPoint &point : new_points)
point.speed = speed;
if (FILAMENT_CONFIG(enable_overhang_bridge_fan) && m_enable_cooling_markers) {
if (!NOZZLE_CONFIG(enable_overhang_speed))
for (ProcessedPoint &point : new_points)
point.speed = speed;
variable_speed = new_points.size() > 1;
}
}
+11 -10
View File
@@ -1024,28 +1024,29 @@ std::string CoolingBuffer::apply_layer_cooldown(
}
if (need_set_fan) {
const auto set_fan = [&](int speed) {
if (m_current_fan_speed != speed) {
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, speed, part_cooling_fan_min_pwm);
m_current_fan_speed = speed;
}
};
if (fan_speed_change_requests[CoolingLine::TYPE_OVERHANG_FAN_START]){
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, overhang_fan_speed, part_cooling_fan_min_pwm);
m_current_fan_speed = overhang_fan_speed;
set_fan(overhang_fan_speed);
} else if (fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START]){ // ORCA: Add support for separate internal bridge fan speed control
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, internal_bridge_fan_speed, part_cooling_fan_min_pwm);
m_current_fan_speed = internal_bridge_fan_speed;
set_fan(internal_bridge_fan_speed);
}
else if (fan_speed_change_requests[CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START]){
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, supp_interface_fan_speed, part_cooling_fan_min_pwm);
m_current_fan_speed = supp_interface_fan_speed;
set_fan(supp_interface_fan_speed);
}
else if (fan_speed_change_requests[CoolingLine::TYPE_IRONING_FAN_START]){
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, ironing_fan_speed, part_cooling_fan_min_pwm);
m_current_fan_speed = ironing_fan_speed;
set_fan(ironing_fan_speed);
}
else if(fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] && m_current_fan_speed != -1){
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_current_fan_speed, part_cooling_fan_min_pwm);
fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] = false;
}
else {
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed, part_cooling_fan_min_pwm);
m_current_fan_speed = m_fan_speed;
set_fan(m_fan_speed);
}
need_set_fan = false;
}
+5 -6
View File
@@ -170,14 +170,13 @@ void FanMover::_put_in_middle_G1(std::list<BufferData>::iterator item_to_split,
void FanMover::_print_in_middle_G1(BufferData& line_to_split, float nb_sec, const std::string &line_to_write) {
if (nb_sec < line_to_split.time * 0.1) {
// doesn't really need to be split, print it after
m_process_output += line_to_split.raw + "\n";
// Doesn't need to be split: the insertion point is at the start.
m_process_output += line_to_write + (line_to_write.back() == '\n'?"":"\n");
} else if (nb_sec > line_to_split.time * 0.9) {
// doesn't really need to be split, print it before
//will also print before if line_to_split.time == 0
m_process_output += line_to_write + (line_to_write.back() == '\n' ? "" : "\n");
m_process_output += line_to_split.raw + "\n";
} else if (nb_sec > line_to_split.time * 0.9) {
// Doesn't need to be split: the insertion point is at the end.
m_process_output += line_to_split.raw + "\n";
m_process_output += line_to_write + (line_to_write.back() == '\n' ? "" : "\n");
}else if(line_to_split.raw.size() > 2
&& line_to_split.raw[0] == 'G' && line_to_split.raw[1] == '1' && line_to_split.raw[2] == ' ') {
float percent = nb_sec / line_to_split.time;
+6 -6
View File
@@ -532,7 +532,7 @@ void GCodeProcessor::TimeMachine::calculate_time(GCodeProcessorResult& result, P
const float height = interpolate ? lerp(prev_move.height, curr_move.height, t) : curr_move.height;
// ORCA: Fix issue with flow rate changes being visualized incorrectly
const float mm3_per_mm = curr_move.mm3_per_mm;
const float fan_speed = interpolate ? lerp(prev_move.fan_speed, curr_move.fan_speed, t) : curr_move.fan_speed;
const float fan_speed = curr_move.fan_speed;
const float temperature = interpolate ? lerp(prev_move.temperature, curr_move.temperature, t) : curr_move.temperature;
actual_speed_moves.push_back({
block.move_id,
@@ -563,7 +563,7 @@ void GCodeProcessor::TimeMachine::calculate_time(GCodeProcessorResult& result, P
const float height = interpolate ? lerp(prev_move.height, curr_move.height, t) : curr_move.height;
// ORCA: Fix issue with flow rate changes being visualized incorrectly
const float mm3_per_mm = curr_move.mm3_per_mm;
const float fan_speed = interpolate ? lerp(prev_move.fan_speed, curr_move.fan_speed, t) : curr_move.fan_speed;
const float fan_speed = curr_move.fan_speed;
const float temperature = interpolate ? lerp(prev_move.temperature, curr_move.temperature, t) : curr_move.temperature;
actual_speed_moves.push_back({
block.move_id,
@@ -1273,7 +1273,7 @@ void GCodeProcessor::run_post_process()
// add lines M73 to exported gcode
auto process_line_move = [
// Lambdas, mostly for string formatting, all with an empty capture block.
time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,format_line_exhaust_fan_control,
time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,
&self = std::as_const(m_time_processor),
// Caches, to be modified
&g1_times_cache_it, &last_exported_main, &last_exported_stop,
@@ -5304,7 +5304,7 @@ void GCodeProcessor::process_VG1(const GCodeReader::GCodeLine& line)
float filament_radius = 0.5f * filament_diameter;
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
auto absolute_position = [this, area_filament_cross_section](Axis axis, const GCodeReader::GCodeLine& lineG1) {
auto absolute_position = [this](Axis axis, const GCodeReader::GCodeLine& lineG1) {
bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
if (axis == E)
is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
@@ -5755,7 +5755,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc
if (travel_length < 0.001)
return;
auto adjust_target = [this, area_filament_cross_section](const AxisCoords& target, const AxisCoords& prev_position) {
auto adjust_target = [this](const AxisCoords& target, const AxisCoords& prev_position) {
AxisCoords ret = target;
if (m_global_positioning_type == EPositioningType::Relative) {
for (unsigned char a = X; a <= E; ++a) {
@@ -7006,7 +7006,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type,
get_acceleration(normal_mode));
const float junction_deviation = get_option_value(m_time_processor.machine_limits.machine_max_junction_deviation, normal_mode_id);
const bool use_jd_jerk = (m_flavor == gcfMarlinFirmware && junction_deviation > 0.0f);
const auto axis_jerk_for_preview = [this, normal_mode, use_jd_jerk, move_acceleration](Axis axis) {
const auto axis_jerk_for_preview = [this, use_jd_jerk, move_acceleration](Axis axis) {
return use_jd_jerk ? get_axis_max_jerk_with_jd(normal_mode, axis, move_acceleration) : get_axis_max_jerk(normal_mode, axis);
};
const float jerk_x = axis_jerk_for_preview(X);
+1 -1
View File
@@ -78,7 +78,7 @@ struct PlateBBoxData
int first_extruder = 0;
float nozzle_diameter = 0.4;
std::string bed_type;
float first_layer_time;
float first_layer_time = 0.0f;
// version 1: use view type ColorPrint (filament color)
// version 2: use view type FilamentId (filament id)
int version = 2;
+2 -2
View File
@@ -4083,7 +4083,7 @@ void WipeTower::toolchange_wipe_new(WipeTowerWriter &writer, const box_coordinat
}
return time * 60.f;
};
auto estimate_wipe_time = [&estimate_time_kernel, & cleaning_box, &target_speed, &x_to_wipe, &xr, &xl, &dy, &WipeSpeedMap, &solid_tool_toolchange](int begin_line) -> float {
auto estimate_wipe_time = [&estimate_time_kernel, & cleaning_box, &x_to_wipe, &xr, &xl, &dy, &solid_tool_toolchange](int begin_line) -> float {
int n = std::ceil(x_to_wipe / (xr - xl));
if (solid_tool_toolchange) n = (cleaning_box.lu[1] - cleaning_box.ld[1]) / dy;
float total_time = estimate_time_kernel(n);
@@ -5111,7 +5111,7 @@ Polygon WipeTower::generate_rib_polygon(const box_coordinates &wt_box)
Polygon WipeTower::generate_support_wall_new(WipeTowerWriter &writer, const box_coordinates &wt_box, double feedrate, bool first_layer,bool rib_wall, bool extrude_perimeter, bool skip_points)
{
auto get_closet_idx = [this, &writer](Polylines &pls) -> std::pair<int,int> {
auto get_closet_idx = [&writer](Polylines &pls) -> std::pair<int,int> {
Vec2f anchor{writer.x(), writer.y()};
int closestIndex = -1;
int closestPl = -1;
-1
View File
@@ -165,7 +165,6 @@ public:
//BBS
unsigned int m_last_additional_fan_speed;
int m_last_bed_temperature;
bool m_last_bed_temperature_reached;
double m_lifted;
-1
View File
@@ -125,7 +125,6 @@ public:
/******************************************** Splitting the 128bit number into two 64bit words *********************************************/
Int128(int64_t lo = 0) : m_lo((uint64_t)lo), m_hi((lo < 0) ? -1 : 0) {}
Int128(const Int128 &val) : m_lo(val.m_lo), m_hi(val.m_hi) {}
Int128(const int64_t& hi, const uint64_t& lo) : m_lo(lo), m_hi(hi) {}
Int128& operator = (const int64_t &val)
+1
View File
@@ -419,6 +419,7 @@ coordf_t Layer::get_sparse_infill_max_void_area()
double spacing = flow.scaled_spacing() * (100 - density) / density;
switch (pattern) {
case ipConcentric:
case ipSpiralInset:
case ipRectilinear:
case ipLine:
case ipGyroid:
-9
View File
@@ -33,15 +33,6 @@ public:
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();
+1 -1
View File
@@ -880,7 +880,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
// Revert the centering operation.
trafo_volume.set_offset(trafo_volume.get_offset() - o->origin_translation);
int counter = 1;
auto copy_volume = [o, v, max_extruders, &counter, &extruder_counter](ModelVolume *new_v) {
auto copy_volume = [o, v, &counter](ModelVolume *new_v) {
assert(new_v != nullptr);
new_v->name = (counter > 1) ? o->name + "_" + std::to_string(counter++) : o->name;
//BBS: Use extruder priority: volumn > object > default
+1
View File
@@ -24,6 +24,7 @@ public:
explicit MultiPoint(const Points &_points) : points(_points) {}
MultiPoint& operator=(const MultiPoint &other) { points = other.points; return *this; }
MultiPoint& operator=(MultiPoint &&other) { points = std::move(other.points); return *this; }
virtual ~MultiPoint() = default;
void scale(double factor);
void scale(double factor_x, double factor_y);
void translate(double x, double y) { this->translate(Point(coord_t(x), coord_t(y))); }
-1
View File
@@ -39,7 +39,6 @@ namespace orientation {
float height_to_bottom_hull_ratio = 0; // affects stability, the lower the better
float unprintability = 0;
Eigen::VectorXf areas_cooling;
CostItems(CostItems const & other) = default;
CostItems() = default;
static std::string field_names() {
return " overhang, bottom, bothull, contour, A_laf, A_prj, unprintability";
+1 -1
View File
@@ -2127,7 +2127,7 @@ void PerimeterGenerator::process_no_bridge(Surfaces& all_surfaces, coord_t perim
bridgeable_filtered = union_ex(offset_ex(remaining, perimeter_spacing), bridgeable_filtered);
bridgeable_filtered = offset_ex(bridgeable_filtered, -perimeter_spacing);
bridgeable_filtered = diff_ex(bridgeable_filtered, remaining, ApplySafetyOffset::Yes);
bridgeable_filtered = opening_ex(bridgeable_filtered, perimeter_spacing); // filter noise from the diff_ex
bridgeable_filtered = opening_ex(bridgeable_filtered, ext_perimeter_width / 2); // filter noise from the diff_ex
bridgeable_filtered = offset_ex(bridgeable_filtered, perimeter_spacing); // restore the size to the original bridgeable area
// Safety measure: Keep the bridge mask from intruding deeper into the
// supported anchor region than the explicit anchor overlap.
-2
View File
@@ -195,7 +195,6 @@ public:
Point(int64_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
Point(int32_t x, int64_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
Point(double x, double y) : Vec2crd(coord_t(std::round(x)), coord_t(std::round(y))) {}
Point(const Point &rhs) { *this = rhs; }
explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(std::round(rhs.x())), coord_t(std::round(rhs.y()))) {}
// This constructor allows you to construct Point from Eigen expressions
// This constructor has to be implicit (non-explicit) to allow implicit conversion from Eigen expressions.
@@ -278,7 +277,6 @@ public:
Point3(int32_t x, int32_t y, int32_t z = 0) : Vec3crd(coord_t(x), coord_t(y), coord_t(z)) {}
Point3(int64_t x, int64_t y, int64_t z = 0) : Vec3crd(coord_t(x), coord_t(y), coord_t(z)) {}
Point3(double x, double y, double z = 0.0) : Vec3crd(coord_t(std::round(x)), coord_t(std::round(y)), coord_t(std::round(z))) {}
Point3(const Point3 &rhs) { *this = rhs; }
explicit Point3(const Vec2crd& vec2crd, coord_t z = 0) : Vec3crd(vec2crd.x(), vec2crd.y(), z) {}
explicit Point3(const Vec3crd &vec3crd) : Vec3crd(vec3crd) {}
// This constructor allows you to construct Point from Eigen expressions
+11 -7
View File
@@ -545,7 +545,7 @@ std::string generate_preset_setting_id(const std::string& vendor, const std::str
return "";
// Dedicated namespace for preset setting_ids, distinct from the cloud per-user
// namespace (OrcaCloudServiceAgent). Keep in sync with scripts/assign_vendor_setting_ids.py;
// namespace (OrcaCloudServiceAgent). Keep in sync with scripts/orca_id_tool.py;
// never change this constant.
static const boost::uuids::uuid vendor_namespace =
boost::uuids::string_generator()("c1f4d9e2-7a3b-5c8d-9e0f-1a2b3c4d5e6f");
@@ -983,15 +983,19 @@ BedType Preset::get_default_bed_type(PresetBundle* preset_bundle)
if (config.has("default_bed_type") && !config.opt_string("default_bed_type").empty()) {
try {
std::string str_bed_type = config.opt_string("default_bed_type");
// Try parsing as integer first (legacy format)
BedType bed_type;
if (ConfigOptionEnum<BedType>::from_string(str_bed_type, bed_type) &&
bed_type > btDefault && bed_type < btCount) {
return bed_type;
}
// Try parsing as integer (legacy format)
int bed_type_value = atoi(str_bed_type.c_str());
if (bed_type_value > 0) {
if (bed_type_value > 0 && bed_type_value < BedType::btCount) {
return BedType(bed_type_value);
}
else {
BOOST_LOG_TRIVIAL(error) << "default_bed_type: invalid bed type: " << str_bed_type;
}
BOOST_LOG_TRIVIAL(error) << "default_bed_type: invalid bed type: " << str_bed_type;
return BedType::btPEI;
} catch(...) {
+9 -9
View File
@@ -93,8 +93,8 @@ class PresetBundle;
// Deterministic preset setting_id: uuid5(vendor/type/name) -> 16 base62 chars.
// Pure function of a system preset's identity, so the value can be assigned by
// scripts/assign_vendor_setting_ids.py and recomputed here when a profile ships
// without it. MUST stay byte-identical to scripts/assign_vendor_setting_ids.py.
// scripts/orca_id_tool.py and recomputed here when a profile ships without it.
// MUST stay byte-identical to scripts/orca_id_tool.py.
// This is NOT the per-user cloud-sync setting_id
// (OrcaCloudServiceAgent::generate_uuid_for_setting_id) - do not conflate them.
std::string generate_preset_setting_id(const std::string& vendor,
@@ -840,13 +840,12 @@ public:
protected:
PresetCollection() = default;
// Copy constructor and copy operators are not to be used from outside PresetBundle,
// as the Profile::vendor points to an instance of VendorProfile stored at parent PresetBundle!
PresetCollection(const PresetCollection &other) = default;
//BBS: add operator= logic insteadof default
// Deleted by the std::recursive_mutex member. PresetBundle copies by assignment.
PresetCollection(const PresetCollection &other) = delete;
//BBS: hand-written because m_mutex cannot be copy-assigned.
PresetCollection& operator=(const PresetCollection &other);
// After copying a collection with the default operators above, call this function
// to adjust Profile::vendor pointers.
// Copying leaves every Preset::vendor pointing into the source bundle's vendor map.
// This re-points them at the matching entries in vendors.
void update_vendor_ptrs_after_copy(const VendorMap &vendors);
// Select a preset, if it exists. If it does not exist, select an invalid (-1) index.
@@ -984,7 +983,8 @@ public:
bool only_default_printers() const;
private:
PrinterPresetCollection() = default;
PrinterPresetCollection(const PrinterPresetCollection &other) = default;
// Deleted along with the base copy constructor.
PrinterPresetCollection(const PrinterPresetCollection &other) = delete;
PrinterPresetCollection& operator=(const PrinterPresetCollection &other) = default;
friend class PresetBundle;
+100 -10
View File
@@ -2157,7 +2157,7 @@ int PresetBundle::validate_presets(const std::string &file_name, DynamicPrintCon
void PresetBundle::remove_users_preset(AppConfig &config, std::map<std::string, std::map<std::string, std::string>> *my_presets)
{
auto check_removed = [my_presets, this](Preset &preset) -> bool {
auto check_removed = [my_presets](Preset &preset) -> bool {
if (my_presets == nullptr) return true;
if (my_presets->find(preset.name) != my_presets->end()) return false;
if (!preset.sync_info.empty()) return false; // syncing, not remove
@@ -2920,6 +2920,16 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
// If executed due to a Config Wizard update, preferred_printer contains the first newly installed printer, otherwise nullptr.
const Preset *preferred_printer = printers.find_system_preset_by_model_and_variant(preferred_selection.printer_model_id, preferred_selection.printer_variant);
printers.select_preset_by_name(preferred_printer ? preferred_printer->name : initial_printer_profile_name, true);
Preset &selected_printer = printers.get_edited_preset();
if (selected_printer.printer_technology() == ptFFF) {
BedType bed_type = selected_printer.get_default_bed_type(this);
const std::string saved_bed_type = config.get_printer_setting(selected_printer.name, "curr_bed_type");
const int saved_bed_type_value = atoi(saved_bed_type.c_str());
if (saved_bed_type_value > btDefault && saved_bed_type_value < btCount)
bed_type = static_cast<BedType>(saved_bed_type_value);
project_config.set_key_value("curr_bed_type", new ConfigOptionEnum<BedType>(bed_type));
config.set("curr_bed_type", std::to_string(static_cast<int>(bed_type)));
}
CNumericLocalesSetter locales_setter;
// Orca: load from orca_presets
@@ -3373,6 +3383,24 @@ std::vector<size_t> PresetBundle::physical_filament_config_indices() const
}
// Orca: the AMS lookups below resolve a tray's filament_id to the FIRST compatible base
// preset. When several presets match the same id for the selected printer the pick is
// arbitrary (a profile bug - see the validator's check_duplicate_filament_subtypes), so
// scan past a successful match and warn about the runners-up. Behavior is unchanged.
static void warn_ambiguous_filament_id_match(const PresetCollection &filaments, PresetCollection::ConstIterator match, const std::string &filament_id)
{
if (match == filaments.end())
return;
std::string others;
for (auto it = std::next(match); it != filaments.end(); ++it)
if (it->is_compatible && filaments.get_preset_base(*it) == &*it && it->filament_id == filament_id)
others += (others.empty() ? "\"" : ", \"") + it->name + "\"";
if (!others.empty())
BOOST_LOG_TRIVIAL(warning) << "Ambiguous AMS filament match: filament_id \"" << filament_id
<< "\" matches multiple presets compatible with the selected printer; picked \"" << match->name
<< "\", also matches " << others;
}
void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info)
{
combox_info.clear();
@@ -3395,6 +3423,7 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info)
}
auto iter = std::find_if(filaments.begin(), filaments.end(),
[this, &filament_id](auto &f) { return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; });
warn_ambiguous_filament_id_match(filaments, iter, filament_id);
if (iter == filaments.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id;
auto filament_type = ams.opt_string("filament_type", 0u);
@@ -3412,7 +3441,7 @@ void PresetBundle::get_ams_cobox_infos(AMSComboInfo& combox_info)
combox_info.ams_names.push_back(ams_name);
continue;
}
iter = std::find_if(filaments.begin(), filaments.end(), [&filament_type](auto &f) { return f.is_compatible && f.is_system; });
iter = std::find_if(filaments.begin(), filaments.end(), [](auto &f) { return f.is_compatible && f.is_system; });
if (iter == filaments.end())
continue;
}
@@ -3497,6 +3526,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
auto iter = std::find_if(filaments.begin(), filaments.end(), [this, &filament_id, &has_type, filament_type](auto &f) {
has_type |= f.config.opt_string("filament_type", 0u) == filament_type;
return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; });
warn_ambiguous_filament_id_match(filaments, iter, filament_id);
if (iter == filaments.end()) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id;
if (!filament_type.empty()) {
@@ -4009,6 +4039,9 @@ std::vector<std::vector<DynamicPrintConfig>> PresetBundle::get_extruder_filament
return filament_infos;
}
// ORCA TODO: currently, this function assumes the printer name follows the pattern of "<printer_model> <nozzle_diameter>", e.g.
// printer_type: "Bambu Lab X2D", nozzle_diameter_str: "0.4 nozzle" => printer_name: "Bambu Lab X2D 0.4 nozzle". If the printer name does
// not follow this pattern, the function may not work correctly.
std::set<std::string> PresetBundle::get_printer_names_by_printer_type_and_nozzle(const std::string &printer_type, std::string nozzle_diameter_str, bool system_only)
{
std::set<std::string> printer_names;
@@ -4039,6 +4072,40 @@ std::set<std::string> PresetBundle::get_printer_names_by_printer_type_and_nozzle
return printer_names;
}
std::vector<Preset *> PresetBundle::get_filament_presets_for_machine(const std::string &printer_type,
const std::string &nozzle_diameter_str,
bool include_user_presets)
{
// Printer model plus nozzle diameter is expected to resolve to a single system printer preset;
// get_printer_names_by_printer_type_and_nozzle asserts as much in debug builds.
const std::set<std::string> printer_names = get_printer_names_by_printer_type_and_nozzle(printer_type, nozzle_diameter_str);
const Preset *printer = printer_names.empty() ? nullptr : printers.find_preset(*printer_names.begin());
if (printer == nullptr)
return {};
// Preset::is_visible is deliberately not consulted: it tracks what the Configuration Wizard
// installed, while the caller identifies a physically connected machine the user may never
// have installed - gating on it would empty the list for exactly those machines.
const PresetWithVendorProfile active_printer = printers.get_preset_with_vendor_profile(*printer);
// Loop invariant - the two argument is_compatible_with_printer() would rebuild it per preset.
DynamicPrintConfig printer_config;
printer_config.set_key_value("printer_preset", new ConfigOptionString(printer->name));
if (const ConfigOption *opt = printer->config.option("nozzle_diameter"))
printer_config.set_key_value("num_extruders", new ConfigOptionInt((int) static_cast<const ConfigOptionFloats *>(opt)->values.size()));
std::vector<Preset *> compatible;
for (Preset &preset : filaments) {
/* The situation where the preset is not offered is as follows:
1. Not a root preset
2. Not a system preset and the printer firmware does not support user presets */
if (filaments.get_preset_base(preset) != &preset || (!preset.is_system && !include_user_presets))
continue;
if (is_compatible_with_printer(filaments.get_preset_with_vendor_profile(preset), active_printer, &printer_config))
compatible.push_back(&preset);
}
return compatible;
}
bool PresetBundle::check_filament_temp_equation_by_printer_type_and_nozzle_for_mas_tray(
const std::string &printer_type, std::string& nozzle_diameter_str, std::string &setting_id, std::string &tag_uid, std::string &nozzle_temp_min, std::string &nozzle_temp_max, std::string& preset_setting_id)
{
@@ -4047,7 +4114,11 @@ bool PresetBundle::check_filament_temp_equation_by_printer_type_and_nozzle_for_m
std::map<std::string, std::vector<Preset const *>> filament_list = filaments.get_filament_presets();
std::set<std::string> printer_names = get_printer_names_by_printer_type_and_nozzle(printer_type, nozzle_diameter_str);
for (const Preset *preset : filament_list.find(setting_id)->second) {
auto filament_iter = filament_list.find(setting_id);
if (filament_iter == filament_list.end())
return is_equation;
for (const Preset *preset : filament_iter->second) {
if (tag_uid == "0" || (tag_uid.size() == 16 && tag_uid.substr(12, 2) == "01")) continue;
if (preset && !preset->is_user()) continue;
ConfigOption * printer_opt = const_cast<Preset *>(preset)->config.option("compatible_printers");
@@ -5222,8 +5293,8 @@ std::string PresetBundle::load_vendor_preset(
loaded.description = entry.description;
loaded.setting_id = entry.setting_id;
// Derive the preset setting_id on the fly when a profile ships without one,
// matching scripts/assign_vendor_setting_ids.py. Only instantiated presets
// carry an id; non-instantiated base profiles return earlier above. This never
// matching scripts/orca_id_tool.py. Only instantiated presets carry an id;
// non-instantiated base profiles return earlier above. This never
// touches the per-user cloud-sync setting_id written into user .info files.
if (loaded.setting_id.empty() && entry.instantiation == "true")
loaded.setting_id = generate_preset_setting_id(
@@ -6160,7 +6231,11 @@ bool PresetBundle::check_duplicate_filament_subtypes() const
// inherited from its @base at load time), grouped by vendor so we only test a
// printer against its own vendor's filaments. A vendor's compatible_printers
// only names that vendor's printers, so same-vendor scoping is correctness
// preserving and avoids an O(all printers x all filaments) sweep.
// preserving and avoids an O(all printers x all filaments) sweep. The one
// exception is the Orca Filament Library: its presets have empty
// compatible_printers (= compatible with every printer, minus the alias-shadowing
// exclusions that is_compatible_with_printer checks via m_excluded_from), so they
// are tested against every vendor's printers as well.
std::map<std::string, std::vector<const Preset *>> filaments_by_vendor;
for (const auto &preset : filaments) {
if (!preset.is_system || preset.filament_id.empty() || preset.vendor == nullptr)
@@ -6168,20 +6243,29 @@ bool PresetBundle::check_duplicate_filament_subtypes() const
filaments_by_vendor[preset.vendor->name].push_back(&preset);
}
const std::vector<const Preset *> no_filaments;
const auto library_it = filaments_by_vendor.find(ORCA_FILAMENT_LIBRARY);
const std::vector<const Preset *> &library_filaments = library_it == filaments_by_vendor.end() ? no_filaments : library_it->second;
bool found_duplicates = false;
for (const auto &printer : printers) {
if (!printer.is_system || printer.vendor == nullptr)
continue;
auto vendor_it = filaments_by_vendor.find(printer.vendor->name);
if (vendor_it == filaments_by_vendor.end())
const std::vector<const Preset *> &vendor_filaments = vendor_it == filaments_by_vendor.end() ? no_filaments : vendor_it->second;
if (vendor_filaments.empty() && library_filaments.empty())
continue;
const PresetWithVendorProfile active_printer = printers.get_preset_with_vendor_profile(printer);
// std::map keeps the reported errors in a deterministic (sorted) order.
std::map<std::string, std::vector<const Preset *>> by_filament_id;
for (const Preset *fil : vendor_it->second)
for (const Preset *fil : vendor_filaments)
if (is_compatible_with_printer(filaments.get_preset_with_vendor_profile(*fil), active_printer))
by_filament_id[fil->filament_id].push_back(fil);
if (&vendor_filaments != &library_filaments)
for (const Preset *fil : library_filaments)
if (is_compatible_with_printer(filaments.get_preset_with_vendor_profile(*fil), active_printer))
by_filament_id[fil->filament_id].push_back(fil);
for (const auto &entry : by_filament_id) {
if (entry.second.size() < 2)
@@ -6189,9 +6273,15 @@ bool PresetBundle::check_duplicate_filament_subtypes() const
found_duplicates = true;
// List each conflicting preset with a clickable file:// URI on its own
// line, so the profile author can jump straight to the files to fix.
// A preset from another bundle (the Orca Filament Library) is tagged with
// its vendor so the source bundle is obvious.
std::string presets;
for (const Preset *p : entry.second)
presets += "\n - " + p->name + "\n " + preset_file_uri(p->file);
for (const Preset *p : entry.second) {
presets += "\n - " + p->name;
if (p->vendor != nullptr && p->vendor->name != printer.vendor->name)
presets += " [" + p->vendor->name + "]";
presets += "\n " + preset_file_uri(p->file);
}
BOOST_LOG_TRIVIAL(error)
<< "Ambiguous AMS filament match: " << entry.second.size()
<< " filament presets share filament_id \"" << entry.first
+7
View File
@@ -350,6 +350,13 @@ public:
std::vector<std::vector<DynamicPrintConfig>> get_extruder_filament_info() const;
std::set<std::string> get_printer_names_by_printer_type_and_nozzle(const std::string &printer_type, std::string nozzle_diameter_str, bool system_only = true);
// Orca: the root filament presets a connected machine can use, resolved with the rule the rest
// of the app applies (is_compatible_with_printer): an empty compatible_printers means every
// printer, minus the alias shadowing exclusions the Orca Filament Library records in
// Preset::m_excluded_from.
std::vector<Preset *> get_filament_presets_for_machine(const std::string &printer_type,
const std::string &nozzle_diameter_str,
bool include_user_presets);
bool check_filament_temp_equation_by_printer_type_and_nozzle_for_mas_tray(const std::string &printer_type,
std::string & nozzle_diameter_str,
std::string & setting_id,
+7 -2
View File
@@ -1,3 +1,8 @@
#ifdef _WIN32
// Keep this first. A header below reaches boost/regex, whose w32_regex_traits
// needs the Win32 types declared already.
#include <Windows.h>
#endif
#include "Config.hpp"
#include "Exception.hpp"
#include "Print.hpp"
@@ -2288,7 +2293,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
obj->clear_shared_object();
//add the print_object share check logic
auto is_print_object_the_same = [this](const PrintObject* object1, const PrintObject* object2) -> bool{
auto is_print_object_the_same = [](const PrintObject* object1, const PrintObject* object2) -> bool{
if (object1->trafo().matrix() != object2->trafo().matrix())
return false;
const ModelObject* model_obj1 = object1->model_object();
@@ -5699,7 +5704,7 @@ int Print::load_cached_data(const std::string& directory)
return CLI_IMPORT_CACHE_NOT_FOUND;
}
auto find_region = [this](PrintObject* object, size_t config_hash) -> const PrintRegion* {
auto find_region = [](PrintObject* object, size_t config_hash) -> const PrintRegion* {
int regions_count = object->num_printing_regions();
for (int index = 0; index < regions_count; index++ )
{
+5 -3
View File
@@ -560,9 +560,11 @@ static inline bool model_volume_solid_or_modifier(const ModelVolume &mv)
static inline Transform3f trafo_for_bbox(const Transform3d &object_trafo, const Transform3d &volume_trafo)
{
Transform3d m = object_trafo * volume_trafo;
m.translation().x() = 0.;
m.translation().y() = 0.;
// Orca: Keep the volume's local XY offset for multipart overlap checks, but remove the object's bed placement.
Transform3d object_trafo_local = object_trafo;
object_trafo_local.translation().x() = 0.;
object_trafo_local.translation().y() = 0.;
Transform3d m = object_trafo_local * volume_trafo;
return m.cast<float>();
}
+87 -37
View File
@@ -275,6 +275,7 @@ static t_config_enum_values s_keys_map_InfillPattern {
{ "tpmsfk", ipTpmsFK },
{ "gyroid", ipGyroid },
{ "concentric", ipConcentric },
{ "spiralinset", ipSpiralInset },
{ "hilbertcurve", ipHilbertCurve },
{ "archimedeanchords", ipArchimedeanChords },
{ "octagramspiral", ipOctagramSpiral }
@@ -371,6 +372,7 @@ static t_config_enum_values s_keys_map_SupportMaterialInterfacePattern {
{ "auto", smipAuto },
{ "rectilinear", smipRectilinear },
{ "concentric", smipConcentric },
{ "spiralinset", smipSpiralInset },
{ "rectilinear_interlaced", smipRectilinearInterlaced},
{ "grid", smipGrid }
};
@@ -1130,8 +1132,7 @@ void PrintConfigDef::init_fff_params()
// BBS
def = this->add("supertack_plate_temp", coInts);
def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers except the initial one. "
"A value of 0 means the filament does not support printing on the Cool Plate SuperTack.");
def->tooltip = L("This is the bed temperature for layers except for the first one. A value of 0 means the filament does not support printing on the Cool Plate SuperTack.");
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
def->full_label = L("Bed temperature");
def->min = 0;
@@ -2293,6 +2294,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("rectilinear");
def->enum_values.push_back("alignedrectilinear");
def->enum_values.push_back("concentric");
def->enum_values.push_back("spiralinset");
def->enum_values.push_back("hilbertcurve");
def->enum_values.push_back("archimedeanchords");
def->enum_values.push_back("octagramspiral");
@@ -2301,6 +2303,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Aligned Rectilinear"));
def->enum_labels.push_back(L("Concentric"));
def->enum_labels.push_back(L("Spiral Inset"));
def->enum_labels.push_back(L("Hilbert Curve"));
def->enum_labels.push_back(L("Archimedean Chords"));
def->enum_labels.push_back(L("Octagram Spiral"));
@@ -2383,7 +2386,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Top surface fill order");
def->category = L("Strength");
def->tooltip = L("Direction in which top surfaces are filled when using a center-based pattern "
"(Concentric, Archimedean Chords, Octagram Spiral).\n"
"(Concentric, Spiral Inset, Archimedean Chords, Octagram Spiral).\n"
"Outward starts at the center of the surface, so any excess material is pushed "
"towards the edge where it is least visible. Inward starts at the edge and ends "
"with the tight curves at the center.\n"
@@ -2402,7 +2405,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Bottom surface fill order");
def->category = L("Strength");
def->tooltip = L("Direction in which bottom surfaces are filled when using a center-based pattern "
"(Concentric, Archimedean Chords, Octagram Spiral).\n"
"(Concentric, Spiral Inset, Archimedean Chords, Octagram Spiral).\n"
"Inward starts each surface with the wider outer curves, which improves first layer "
"adhesion on build plates where the tight curves at the center may not stick. "
"Outward starts at the center, pushing any excess material towards the edge.\n"
@@ -2770,7 +2773,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("fan_cooling_layer_time", coFloats);
def->label = L("Layer time");
def->tooltip = L("The part cooling fan will be enabled for layers where the estimated time is shorter than this value. Fan speed is interpolated between the minimum and maximum fan speeds according to layer printing time.");
def->sidetext = L("s"); // seconds, CIS languages need translation
def->sidetext = L_CONTEXT("s", "second"); // seconds, CIS languages need translation
def->min = 0;
def->max = 1000;
def->mode = comSimple;
@@ -2923,7 +2926,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Filament load time");
def->tooltip = L("Time to load new filament when switch filament. It's usually applicable for single-extruder multi-material machines. "
"For tool changers or multi-tool machines, it's typically 0. For statistics only.");
def->sidetext = L("s"); // seconds, CIS languages need translation
def->sidetext = L_CONTEXT("s", "second"); // seconds, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.0));
@@ -2932,7 +2935,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Filament unload time");
def->tooltip = L("Time to unload old filament when switch filament. It's usually applicable for single-extruder multi-material machines. "
"For tool changers or multi-tool machines, it's typically 0. For statistics only.");
def->sidetext = L("s"); // seconds, CIS languages need translation
def->sidetext = L_CONTEXT("s", "second"); // seconds, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.0));
@@ -2941,7 +2944,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Tool change time");
def->tooltip = L("Time taken to switch tools. It's usually applicable for tool changers or multi-tool machines. "
"For single-extruder multi-material machines, it's typically 0. For statistics only.");
def->sidetext = L("s"); // seconds, CIS languages need translation
def->sidetext = L_CONTEXT("s", "second"); // seconds, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat { 0. });
@@ -3087,7 +3090,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Time to wait after the filament is unloaded. "
"May help to get reliable tool changes with flexible materials "
"that may need more time to shrink to original dimensions.");
def->sidetext = L("s"); // seconds, CIS languages need translation
def->sidetext = L_CONTEXT("s", "second"); // seconds, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0. });
@@ -4206,7 +4209,7 @@ void PrintConfigDef::init_fff_params()
"\nIt won't move fan commands from custom G-code (they act as a sort of 'barrier')."
"\nIt won't move fan commands into the start G-code if the 'only custom start G-code' is activated."
"\nUse 0 to deactivate.");
def->sidetext = L("s"); // seconds, CIS languages need translation
def->sidetext = L_CONTEXT("s", "second"); // seconds, CIS languages need translation
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
@@ -4222,7 +4225,7 @@ void PrintConfigDef::init_fff_params()
"\nThis is useful for fans where a low PWM/power may be insufficient to get the fan started spinning from a stop, or to "
"get the fan up to speed faster."
"\nSet to 0 to deactivate.");
def->sidetext = L("s"); // seconds, CIS languages need translation
def->sidetext = L_CONTEXT("s", "second"); // seconds, CIS languages need translation
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
@@ -4866,7 +4869,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Ironing expansion");
def->category = L("Quality");
def->tooltip = L("Expand or contract the ironing area.");
def->sidetext = L("mm");
def->sidetext = L("mm"); // millimeters, CIS languages need translation
def->min = -100;
def->max = 100;
def->mode = comExpert;
@@ -4903,7 +4906,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Quality");
def->tooltip = L("Minimum Z-layer height.\n"
"Also controls the slicing plane.");
def->sidetext = L("mm");
def->sidetext = L("mm"); // millimeters, CIS languages need translation
def->min = 0;
def->max = 100;
def->mode = comExpert;
@@ -5102,7 +5105,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Machine limits");
def->readonly = false;
def->tooltip = L("The allowed maximum output force of Y axis");
def->sidetext = L("N");
def->sidetext = L_CONTEXT("N", "Newton"); // Newtons, CIS languages need translation
def->min = 0;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloat(0));
@@ -5112,7 +5115,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Machine limits");
def->readonly = false;
def->tooltip = L("The machine bed mass load of Y axis");
def->sidetext = L("g");
def->sidetext = L_CONTEXT("g", "gram"); // grams, CIS languages need translation
def->min = 0;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloat(0));
@@ -5122,7 +5125,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Machine limits");
def->readonly = false;
def->tooltip = L("The allowed max printed mass on a plate");
def->sidetext = L("g");
def->sidetext = L_CONTEXT("g", "gram"); // grams, CIS languages need translation
def->min = 0;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloat(0));
@@ -5481,7 +5484,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("reduce_infill_retraction", coBool);
def->label = L("Reduce infill retraction");
def->tooltip = L("Don\'t retract when the travel is entirely within an infill area. That means the oozing can\'t been seen. This can reduce times of retraction for complex model and save printing time, but make slicing and G-code generating slower. Note that z-hop is also not performed in areas where retraction is skipped.");
def->tooltip = L("Don\'t retract when the travel is entirely within an infill area. That means the oozing can\'t been seen. This can reduce times of retraction for complex model and save printing time, but make slicing and G-code generating slower. Note that Z-hop is also not performed in areas where retraction is skipped.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
@@ -5736,7 +5739,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("retract_after_wipe", coPercents);
def->label = L("Retract amount after wipe");
// xgettext:no-c-format, no-boost-format
def->tooltip = L("The length of fast retraction after wipe, relative to retraction length.\n"
def->tooltip = L("This is the length of fast retraction after wipe, relative to retraction length.\n"
"The value will be clamped by 100% minus the retract amount before the wipe value.");
def->sidetext = "%";
def->mode = comExpert;
@@ -5802,7 +5805,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("z_hop", coFloats);
def->label = L("Z-hop height");
def->tooltip = L("Whenever there is a retraction, the nozzle is lifted a little to create clearance between the nozzle and the print. This prevents the nozzle from hitting the print when traveling more. Using spiral lines to lift z can prevent stringing.");
def->tooltip = L("Whenever there is a retraction, the nozzle is lifted a little to create clearance between the nozzle and the print. This prevents the nozzle from hitting the print when traveling more. Using spiral lines to lift Z can prevent stringing.");
def->sidetext = L("mm"); // millimeters, CIS languages need translation
def->mode = comSimple;
def->min = 0;
@@ -6380,7 +6383,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Layer time");
def->tooltip = L("The printing speed in exported G-code will be slowed down when the estimated layer time is "
"shorter than this value in order to get better cooling for these layers.");
def->sidetext = L("s"); // seconds, CIS languages need translation
def->sidetext = L_CONTEXT("s", "second"); // seconds, CIS languages need translation
def->min = 0;
def->max = 1000;
def->mode = comSimple;
@@ -6533,7 +6536,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Preheat time");
def->tooltip = L("To reduce the waiting time after tool change, Orca can preheat the next tool while the current tool is still in use. "
"This setting specifies the time in seconds to preheat the next tool. Orca will insert a M104 command to preheat the tool in advance.");
def->sidetext = L("s"); // seconds, CIS languages need translation
def->sidetext = L_CONTEXT("s", "second"); // seconds, CIS languages need translation
def->min = 0;
def->max = 120;
def->mode = comAdvanced;
@@ -6964,11 +6967,13 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("auto");
def->enum_values.push_back("rectilinear");
def->enum_values.push_back("concentric");
def->enum_values.push_back("spiralinset");
def->enum_values.push_back("rectilinear_interlaced");
def->enum_values.push_back("grid");
def->enum_labels.push_back(L("Default"));
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Concentric"));
def->enum_labels.push_back(L("Spiral Inset"));
def->enum_labels.push_back(L("Rectilinear Interlaced"));
def->enum_labels.push_back(L("Grid"));
def->mode = comAdvanced;
@@ -8025,7 +8030,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("machine_hotend_change_time", coFloat);
def->label = L("Hotend change time");
def->tooltip = L("Time to change hotend.");
def->sidetext = L("s");
def->sidetext = L_CONTEXT("s", "second");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.0));
@@ -9866,7 +9871,15 @@ std::string DynamicPrintConfig::get_filament_type(std::string &displayed_filamen
auto* filament_type = dynamic_cast<const ConfigOptionStrings*>(this->option("filament_type"));
auto* filament_is_support = dynamic_cast<const ConfigOptionBools*>(this->option("filament_is_support"));
if (!filament_type)
// get_at() on an empty vector option is undefined behavior (.front() of an empty vector),
// and e.g. filament_id is never populated on a CLI from-scratch slice - treat an empty
// option the same as a missing one.
if (filament_id && filament_id->values.empty())
filament_id = nullptr;
if (filament_is_support && filament_is_support->values.empty())
filament_is_support = nullptr;
if (!filament_type || filament_type->values.empty())
return "";
if (!filament_is_support) {
@@ -10091,6 +10104,10 @@ int DynamicPrintConfig::update_values_from_single_to_multi(DynamicPrintConfig& m
for (int index = 0; index < variant_count; index++)
{
//variant_count is the variant column width, src_opt the value array;
//they disagree when the source was authored at a different width
if (index >= (int)src_opt->values.size())
break;
if (opt->values[index] > src_opt->values[index])
opt->values[index] = src_opt->values[index];
}
@@ -10108,6 +10125,8 @@ int DynamicPrintConfig::update_values_from_single_to_multi(DynamicPrintConfig& m
for (int index = 0; index < variant_count; index++)
{
if (index >= (int)src_opt->values.size())
break;
if (opt->values[index].value > src_opt->values[index].value)
opt->values[index] = src_opt->values[index];
}
@@ -10302,6 +10321,10 @@ int DynamicPrintConfig::update_values_from_multi_to_multi(DynamicPrintConfig& ne
for(auto idx : variant_indices){
assert(idx < old_count);
//the counts come from the variant columns, the arrays from the options;
//they disagree when a config was authored at a different variant width
if (idx >= old_count || new_variant_index >= (int)opt->values.size())
continue;
if (old_values[idx] < opt->values[new_variant_index])
opt->values[new_variant_index] = old_values[idx];
}
@@ -10332,6 +10355,10 @@ int DynamicPrintConfig::update_values_from_multi_to_multi(DynamicPrintConfig& ne
for(auto idx : variant_indices){
assert(idx < old_count);
//the counts come from the variant columns, the arrays from the options;
//they disagree when a config was authored at a different variant width
if (idx >= old_count || new_variant_index >= (int)opt->values.size())
continue;
if (old_values[idx] < opt->values[new_variant_index])
opt->values[new_variant_index] = old_values[idx];
}
@@ -10362,6 +10389,8 @@ int DynamicPrintConfig::update_values_from_multi_to_multi(DynamicPrintConfig& ne
for(auto idx : variant_indices){
assert(idx < old_count);
if (idx >= old_count || new_variant_index >= (int)opt->values.size())
continue;
if (old_values[idx]) //enabled
opt->values[new_variant_index] = old_values[idx];
}
@@ -10402,6 +10431,15 @@ int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector<st
same_variant_indices.emplace_back(indices);
}
//dst_values below is the destination PRINT preset's per-variant row, sized to its own
//print_extruder_variant; dst_extruder_variants is the PRINTER's list. They disagree until
//the print preset is re-selected, so size the row to the variant count before indexing it.
const size_t dst_variant_count = dst_extruder_variants.size();
if (dst_variant_count == 0) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", Line %1%: empty destination variant list")%__LINE__;
return -1;
}
t_config_option_keys keys = this->keys();
for(auto& key : keys){
if(key_sets.find(key) == key_sets.end())
@@ -10417,7 +10455,13 @@ int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector<st
{
ConfigOptionFloatsNullable* opt = this->option<ConfigOptionFloatsNullable>(key);
auto src_values = opt->values;
auto dst_values = dst_config.option<ConfigOptionFloatsNullable>(key) ->values;
const auto* dst_opt = dst_config.option<ConfigOptionFloatsNullable>(key);
if(!dst_opt){
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: %2% missing from destination config")%__LINE__%key;
break;
}
auto dst_values = dst_opt->values;
dst_values.resize(dst_variant_count, ConfigOptionFloatsNullable::nil_value());
for(size_t dst_idx =0; dst_idx < same_variant_indices.size(); ++dst_idx){
auto& indices = same_variant_indices[dst_idx];
if(indices.empty())
@@ -10425,7 +10469,7 @@ int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector<st
bool has_value = false;
double target_value = std::numeric_limits<double>::max();
for(auto idx : indices){
if(opt && idx < opt->values.size() && !opt->is_nil(idx)){
if(idx < (int)opt->values.size() && !opt->is_nil(idx)){
has_value = true;
target_value = std::min(target_value, src_values[idx]);
}
@@ -10441,7 +10485,13 @@ int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector<st
{
ConfigOptionFloatsOrPercentsNullable* opt = this->option<ConfigOptionFloatsOrPercentsNullable>(key);
auto src_values = opt->values;
auto dst_values = dst_config.option<ConfigOptionFloatsOrPercentsNullable>(key) ->values;
const auto* dst_opt = dst_config.option<ConfigOptionFloatsOrPercentsNullable>(key);
if(!dst_opt){
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: %2% missing from destination config")%__LINE__%key;
break;
}
auto dst_values = dst_opt->values;
dst_values.resize(dst_variant_count, ConfigOptionFloatsOrPercentsNullable::nil_value());
for(size_t dst_idx =0; dst_idx < same_variant_indices.size(); ++dst_idx){
auto& indices = same_variant_indices[dst_idx];
if(indices.empty())
@@ -10449,7 +10499,7 @@ int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector<st
bool has_value = false;
FloatOrPercent target_value{9999.f, true};
for(auto idx : indices){
if(opt && !opt->is_nil(idx)){
if(idx < (int)opt->values.size() && !opt->is_nil(idx)){
has_value = true;
target_value = src_values[idx].value < target_value.value ? src_values[idx] : target_value;
}
@@ -10465,15 +10515,21 @@ int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector<st
{
ConfigOptionBoolsNullable* opt = this->option<ConfigOptionBoolsNullable>(key);
auto src_values = opt->values;
auto dst_values = dst_config.option<ConfigOptionBoolsNullable>(key) ->values;
const auto* dst_opt = dst_config.option<ConfigOptionBoolsNullable>(key);
if(!dst_opt){
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", Line %1%: %2% missing from destination config")%__LINE__%key;
break;
}
auto dst_values = dst_opt->values;
dst_values.resize(dst_variant_count, ConfigOptionBoolsNullable::nil_value());
for(size_t dst_idx =0; dst_idx < same_variant_indices.size(); ++dst_idx){
auto indices = same_variant_indices[dst_idx];
if(indices.empty())
continue;
bool has_value = false;
bool target_value;
bool target_value = false;
for(auto idx : indices){
if(opt && !opt->is_nil(idx)){
if(idx < (int)opt->values.size() && !opt->is_nil(idx)){
has_value = true;
target_value = src_values[idx];
break;
@@ -11948,13 +12004,11 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def = this->add("load_defaultfila", coBool);
def->label = L("Load default filaments");
def->tooltip = L("Load first filament as default for those not loaded.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("min_save", coBool);
def->label = L("Minimum save");
def->tooltip = L("Export 3MF with minimum size.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("mtcpp", coInt);
@@ -11980,7 +12034,6 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def = this->add("normative_check", coBool);
def->label = L("Normative check");
def->tooltip = L("Check the normative items.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(true));
/*def = this->add("help_fff", coBool);
@@ -12247,7 +12300,7 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->cli_params = "level";
def->set_default_value(new ConfigOptionInt(1));
def = this->add("logfile", coInt);
def = this->add("logfile", coString);
def->label = L("Log file");
def->tooltip = L("Redirects debug logging to file.\n");
def->cli_params = "file";
@@ -12295,7 +12348,6 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("skip_modified_gcodes", coBool);
def->label = L("Skip modified G-code in 3MF");
def->tooltip = L("Skip the modified G-code in 3MF from printer or filament presets.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("makerlab_name", coString);
@@ -12325,14 +12377,12 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("allow_newer_file", coBool);
def->label = L("Allow 3MF with newer version to be sliced");
def->tooltip = L("Allow 3MF with newer version to be sliced.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("allow_mix_temp", coBool);
// internal use only, don't need translation
def->label = "Allow filaments with high/low temperature to be printed together";
def->tooltip = "Allow filaments with high/low temperature to be printed together.";
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false));
}
+2 -2
View File
@@ -113,7 +113,7 @@ enum InfillPattern : int {
ipCubic, ipAdaptiveCubic, ipQuarterCubic, ipSupportCubic, ipLightning,
ipHoneycomb, ip3DHoneycomb, ipLateralHoneycomb, ipLateralLattice,
ipCrossHatch, ipTpmsD, ipTpmsFK, ipGyroid,
ipConcentric, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
ipConcentric, ipSpiralInset, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
ipSupportBase, ipConcentricInternal,
ipCount,
};
@@ -271,7 +271,7 @@ enum LongRectrationLevel
};
enum SupportMaterialInterfacePattern {
smipAuto, smipRectilinear, smipConcentric, smipRectilinearInterlaced, smipGrid
smipAuto, smipRectilinear, smipConcentric, smipSpiralInset, smipRectilinearInterlaced, smipGrid
};
// BBS
+1 -1
View File
@@ -2666,7 +2666,7 @@ void PrintObject::bridge_over_infill()
// SECTION to gather and filter surfaces for expanding, and then cluster them by layer
{
tbb::concurrent_vector<CandidateSurface> candidate_surfaces;
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this), &candidate_surfaces, has_lightning_infill](tbb::blocked_range<size_t> r) {
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject *>(this), &candidate_surfaces](tbb::blocked_range<size_t> r) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
const Layer *layer = po->get_layer(lidx);
+12 -5
View File
@@ -304,11 +304,16 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
float z = zs[z_idx];
int idx_first_printable_region = -1;
bool complex = false;
std::vector<int> printable_region_ids;
for (int idx_region = 0; idx_region < int(layer_range.volume_regions.size()); ++ idx_region) {
const PrintObjectRegions::VolumeRegion &region = layer_range.volume_regions[idx_region];
if (region.bbox->min().z() <= z && region.bbox->max().z() >= z) {
if (idx_first_printable_region == -1 && region.model_volume->is_model_part())
if (region.model_volume->is_model_part())
printable_region_ids.push_back(idx_region);
if (idx_first_printable_region == -1 && region.model_volume->is_model_part()) {
idx_first_printable_region = idx_region;
}
else if (idx_first_printable_region != -1) {
// Test for overlap with some other region.
for (int idx_region2 = idx_first_printable_region; idx_region2 < idx_region; ++ idx_region2) {
@@ -324,8 +329,10 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
if (complex)
zs_complex.push_back({ z_idx, z });
else if (idx_first_printable_region >= 0) {
const PrintObjectRegions::VolumeRegion &region = layer_range.volume_regions[idx_first_printable_region];
slices_by_region[region.region->print_object_region_id()][z_idx] = std::move(volume_slices_find_by_id(volume_slices, region.model_volume->id()).slices[z_idx]);
for (int printable_region_id : printable_region_ids) {
const PrintObjectRegions::VolumeRegion &region = layer_range.volume_regions[printable_region_id];
append(slices_by_region[region.region->print_object_region_id()][z_idx], std::move(volume_slices_find_by_id(volume_slices, region.model_volume->id()).slices[z_idx]));
}
}
}
}
@@ -527,7 +534,7 @@ bool groupingVolumes(std::vector<VolumeSlices> objSliceByVolume, std::vector<gro
}
tbb::parallel_for(tbb::blocked_range<int>(0, osvIndex.size()),
[&osvIndex, &objSliceByVolume, &offsetValue, &resolution](const tbb::blocked_range<int>& range) {
[&osvIndex, &objSliceByVolume, &resolution](const tbb::blocked_range<int>& range) {
for (auto k = range.begin(); k != range.end(); ++k) {
for (ExPolygon& poly_ex : objSliceByVolume[osvIndex[k][0]].slices[osvIndex[k][1]])
poly_ex.douglas_peucker(resolution);
@@ -535,7 +542,7 @@ bool groupingVolumes(std::vector<VolumeSlices> objSliceByVolume, std::vector<gro
});
tbb::parallel_for(tbb::blocked_range<int>(0, osvIndex.size()),
[&osvIndex, &objSliceByVolume,&offsetValue, &resolution](const tbb::blocked_range<int>& range) {
[&osvIndex, &objSliceByVolume,&offsetValue](const tbb::blocked_range<int>& range) {
for (auto k = range.begin(); k != range.end(); ++k) {
objSliceByVolume[osvIndex[k][0]].slices[osvIndex[k][1]] = offset_ex(objSliceByVolume[osvIndex[k][0]].slices[osvIndex[k][1]], offsetValue);
}
+1
View File
@@ -1,4 +1,5 @@
#include <functional>
#include <numeric>
#include <optional>
#include <libslic3r/OpenVDBUtils.hpp>
-3
View File
@@ -44,9 +44,6 @@ struct DrainHole
: pos(p), normal(n), radius(r), height(h), failed(fl)
{}
DrainHole(const DrainHole& rhs) :
DrainHole(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {}
bool operator==(const DrainHole &sp) const;
bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); }
+34 -32
View File
@@ -65,11 +65,13 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
const bool smooth_supports = support_params.support_style != smsGrid;
SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first;
SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second;
// The user-facing interface layer counts include the contact layer. Internally,
// contact layers are generated separately, so only the remaining layers are
// projected into intermediate interface/base-interface layers here.
const size_t num_top_interface_layers = support_params.has_top_contacts ? support_params.num_top_interface_layers - 1 : 0;
const size_t num_bottom_interface_layers = support_params.has_bottom_contacts ? support_params.num_bottom_interface_layers - 1 : 0;
// Contacts printed separately consume one requested interface layer. Organic
// bottom contacts are projection seeds and are not printed separately.
const bool organic_tree = support_params.support_style == smsTreeOrganic;
const size_t num_top_interface_layers = support_params.has_top_contacts ?
support_params.num_top_interface_layers - 1 : 0;
const size_t num_bottom_interface_layers = support_params.has_bottom_contacts ?
support_params.num_bottom_interface_layers - (organic_tree ? 0 : 1) : 0;
const size_t num_top_base_interface_layers = std::min(support_params.num_top_base_interface_layers, num_top_interface_layers);
const size_t num_bottom_base_interface_layers = std::min(support_params.num_bottom_base_interface_layers, num_bottom_interface_layers);
const size_t num_top_interface_layers_only = num_top_interface_layers - num_top_base_interface_layers;
@@ -132,7 +134,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
return nullptr;
};
tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
[&bottom_contacts, &top_contacts, &top_interface_layers, &top_base_interface_layers, &intermediate_layers, &insert_layer, &support_params,
[&bottom_contacts, &top_contacts, &top_interface_layers, &top_base_interface_layers, &intermediate_layers, &insert_layer,
num_top_interface_layers, num_bottom_interface_layers, num_top_base_interface_layers, num_bottom_base_interface_layers,
num_top_interface_layers_only, num_bottom_interface_layers_only,
snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) {
@@ -1234,10 +1236,6 @@ static void modulate_extrusion_by_overlapping_layers(
(fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
}
private:
ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {
return *this;
}
const std::vector<ExtrusionPathFragment> &m_path_fragments;
};
const coord_t search_radius = 7;
@@ -1656,28 +1654,32 @@ void generate_support_toolpaths(
if (top_contact_layer.could_merge(interface_layer) && ! raft_layer)
top_contact_layer.merge(std::move(interface_layer));
}
if (!bottom_interfaces && support_params.can_merge_support_regions) {
if (base_layer.could_merge(bottom_contact_layer))
base_layer.merge(std::move(bottom_contact_layer));
else if (base_layer.empty() && ! bottom_contact_layer.empty() && ! bottom_contact_layer.layer->bridging)
base_layer = std::move(bottom_contact_layer);
} else if (bottom_contact_layer.could_merge(top_contact_layer) && ! raft_layer) {
if (top_interfaces && bottom_interfaces) {
top_contact_layer.merge(std::move(bottom_contact_layer));
} else if (bottom_interfaces) {
top_contact_layer.set_polygons_to_extrude(
diff(top_contact_layer.polygons_to_extrude(), bottom_contact_layer.polygons_to_extrude()));
} else {
bottom_contact_layer.set_polygons_to_extrude(
diff(bottom_contact_layer.polygons_to_extrude(), top_contact_layer.polygons_to_extrude()));
}
} else if (bottom_contact_layer.could_merge(interface_layer) && ! organic_tree) {
const bool interface_layer_is_bottom = interface_layer.layer->layer_type == SupporLayerType::BottomInterface;
if (bottom_interfaces && interface_layer_is_bottom) {
bottom_contact_layer.merge(std::move(interface_layer));
} else {
bottom_contact_layer.set_polygons_to_extrude(
diff(bottom_contact_layer.polygons_to_extrude(), interface_layer.polygons_to_extrude()));
// Orca: Organic bottom contacts are projection seeds, not same-layer toolpaths.
// Do not merge them into another same-layer support region.
if (!organic_tree) {
if (!bottom_interfaces && support_params.can_merge_support_regions) {
if (base_layer.could_merge(bottom_contact_layer))
base_layer.merge(std::move(bottom_contact_layer));
else if (base_layer.empty() && ! bottom_contact_layer.empty() && ! bottom_contact_layer.layer->bridging)
base_layer = std::move(bottom_contact_layer);
} else if (bottom_contact_layer.could_merge(top_contact_layer) && ! raft_layer) {
if (top_interfaces && bottom_interfaces) {
top_contact_layer.merge(std::move(bottom_contact_layer));
} else if (bottom_interfaces) {
top_contact_layer.set_polygons_to_extrude(
diff(top_contact_layer.polygons_to_extrude(), bottom_contact_layer.polygons_to_extrude()));
} else {
bottom_contact_layer.set_polygons_to_extrude(
diff(bottom_contact_layer.polygons_to_extrude(), top_contact_layer.polygons_to_extrude()));
}
} else if (bottom_contact_layer.could_merge(interface_layer)) {
const bool interface_layer_is_bottom = interface_layer.layer->layer_type == SupporLayerType::BottomInterface;
if (bottom_interfaces && interface_layer_is_bottom) {
bottom_contact_layer.merge(std::move(interface_layer));
} else {
bottom_contact_layer.set_polygons_to_extrude(
diff(bottom_contact_layer.polygons_to_extrude(), interface_layer.polygons_to_extrude()));
}
}
}
+1 -1
View File
@@ -1588,7 +1588,7 @@ static inline std::tuple<Polygons, Polygons, double> detect_contacts(
// Cache support trimming polygons derived from lower layer polygons, possible merged with "on build plate only" trimming polygons.
auto slices_margin_update =
[&slices_margin, &layer, &lower_layer, &lower_layer_polygons, buildplate_only, has_enforcer, &annotations, layer_id]
[&slices_margin, &lower_layer, &lower_layer_polygons, buildplate_only, has_enforcer, &annotations, layer_id]
(float slices_margin_offset, float no_interface_offset) {
if (slices_margin.offset != slices_margin_offset) {
slices_margin.offset = slices_margin_offset;
@@ -141,6 +141,8 @@ struct SupportParameters {
this->contact_fill_pattern = ipGrid;
else if (object_config.support_interface_pattern == smipRectilinearInterlaced)
this->contact_fill_pattern = ipRectilinear;
else if (object_config.support_interface_pattern == smipSpiralInset)
this->contact_fill_pattern = ipSpiralInset;
else
this->contact_fill_pattern =
(object_config.support_interface_pattern == smipAuto && zero_gap_contact_interface) ||
+1 -1
View File
@@ -32,7 +32,7 @@ namespace Slic3r::TreeSupport3D
using namespace std::literals;
// or warning
// had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL()
// had to use a define because the macro processing inside macro BOOST_LOG_TRIVIAL()
#define error_level_not_in_cache debug
//FIXME Machine border is currently ignored.
+1 -1
View File
@@ -2667,7 +2667,7 @@ void TreeSupport::drop_nodes()
SupportNode::diameter_angle_scale_factor = diameter_angle_scale_factor;
float DO_NOT_MOVER_UNDER_MM = is_slim ? 0 : 5; // do not move contact points under 5mm
auto get_max_move_dist = [this, &config, tan_angle, wall_count, support_extrusion_width](const SupportNode *node, int power = 1) {
auto get_max_move_dist = [this, tan_angle, support_extrusion_width](const SupportNode *node, int power = 1) {
if (node->max_move_dist == 0) {
node->radius = get_radius(node);
node->max_move_dist = std::min(tan_angle * node->height, support_extrusion_width);
+3 -2
View File
@@ -204,8 +204,9 @@ public:
clear_nodes();
}
TreeSupportData(TreeSupportData&&) = default;
TreeSupportData& operator=(TreeSupportData&&) = default;
// Deleted by the tbb::spin_mutex member.
TreeSupportData(TreeSupportData&&) = delete;
TreeSupportData& operator=(TreeSupportData&&) = delete;
TreeSupportData(const TreeSupportData&) = delete;
TreeSupportData& operator=(const TreeSupportData&) = delete;
+2 -2
View File
@@ -28,9 +28,9 @@ std::string CalibPressureAdvance::move_to(Vec2d pt, GCodeWriter &writer, std::st
gcode << writer.retract(); // retract before z move or move
if(z > EPSILON && layer_height >= 0){
gcode << writer.travel_to_z(z, "z-hop"); // Perform z hop
gcode << writer.travel_to_z(z, "Z-hop"); // Perform z hop
gcode << writer.travel_to_xy(pt, comment); // Travel with z move
gcode << writer.travel_to_z(layer_height, "undo z-hop"); // Undo z hop
gcode << writer.travel_to_z(layer_height, "undo Z-hop"); // Undo z hop
}else {
gcode << writer.travel_to_xy(pt, comment);
}
+3 -17
View File
@@ -91,29 +91,15 @@ class CaliPresetInfo
{
public:
int tray_id;
int extruder_id;
NozzleVolumeType nozzle_volume_type;
BedType bed_type;
int extruder_id = 0;
NozzleVolumeType nozzle_volume_type{nvtStandard};
BedType bed_type{btDefault};
float nozzle_diameter;
int nozzle_pos_id{-1};
std::string nozzle_sn;
std::string filament_id;
std::string setting_id;
std::string name;
CaliPresetInfo &operator=(const CaliPresetInfo &other)
{
this->tray_id = other.tray_id;
this->extruder_id = other.extruder_id;
this->nozzle_volume_type = other.nozzle_volume_type;
this->nozzle_diameter = other.nozzle_diameter;
this->nozzle_pos_id = other.nozzle_pos_id;
this->nozzle_sn = other.nozzle_sn;
this->filament_id = other.filament_id;
this->setting_id = other.setting_id;
this->name = other.name;
return *this;
}
};
struct PrinterCaliInfo
-3
View File
@@ -5,9 +5,6 @@
#define SLIC3R_APP_KEY "@SLIC3R_APP_KEY@"
#define SLIC3R_VERSION "@SLIC3R_VERSION@"
#define SoftFever_VERSION "@SoftFever_VERSION@"
#ifndef GIT_COMMIT_HASH
#define GIT_COMMIT_HASH "0000000" // 0000000 means uninitialized
#endif
#define SLIC3R_BUILD_ID "@SLIC3R_BUILD_ID@"
//#define SLIC3R_RC_VERSION "@SLIC3R_VERSION@"
#define BBL_INTERNAL_TESTING @BBL_INTERNAL_TESTING@
+5 -1
View File
@@ -310,7 +310,11 @@ void set_data_dir(const std::string &dir)
{
g_data_dir = dir;
if (!g_data_dir.empty() && !boost::filesystem::exists(g_data_dir)) {
boost::filesystem::create_directory(g_data_dir);
try {
boost::filesystem::create_directories(g_data_dir);
} catch (const boost::filesystem::filesystem_error &ex) {
BOOST_LOG_TRIVIAL(error) << "set_data_dir: failed to create data directory " << g_data_dir << ": " << ex.what();
}
}
}