Merge remote-tracking branch 'remotes/origin/master' into fs_QuadricEdgeCollapse

This commit is contained in:
Vojtech Bubnik 2021-08-05 17:17:18 +02:00
commit ea5a90f08c
42 changed files with 468 additions and 142 deletions

View file

@ -871,6 +871,7 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option
token.erase(equals_pos);
}
}
// Look for the cli -> option mapping.
const auto it = opts.find(token);
if (it == opts.end()) {
@ -879,15 +880,46 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option
}
const t_config_option_key opt_key = it->second;
const ConfigOptionDef &optdef = this->def()->options.at(opt_key);
// If the option type expects a value and it was not already provided,
// look for it in the next token.
if (optdef.type != coBool && optdef.type != coBools && value.empty()) {
if (i == (argc-1)) {
boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl;
if (value.empty()) {
if (optdef.type != coBool && optdef.type != coBools) {
if (i == (argc-1)) {
boost::nowide::cerr << "No value supplied for --" << token.c_str() << std::endl;
return false;
}
value = argv[++ i];
} else {
// This is a bool or bools. The value is optional, but may still be there.
// Check if the next token can be deserialized into ConfigOptionBool.
// If it is in fact bools, it will be rejected later anyway.
if (i != argc-1) { // There is still a token to read.
ConfigOptionBool cobool;
if (cobool.deserialize(argv[i+1]))
value = argv[++i];
}
}
}
if (no) {
if (optdef.type != coBool && optdef.type != coBools) {
boost::nowide::cerr << "Only boolean config options can be negated with --no- prefix." << std::endl;
return false;
}
else if (! value.empty()) {
boost::nowide::cerr << "Boolean options negated by the --no- prefix cannot have a value." << std::endl;
return false;
}
value = argv[++ i];
}
if (optdef.type == coBools && ! value.empty()) {
boost::nowide::cerr << "Vector boolean options cannot have a value. Fill them in by "
"repeating them and negate by --no- prefix." << std::endl;
return false;
}
// Store the option value.
const bool existing = this->has(opt_key);
if (keys != nullptr && ! existing) {
@ -911,7 +943,10 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option
// unescaped by the calling shell.
opt_vector->deserialize(value, true);
} else if (opt_base->type() == coBool) {
static_cast<ConfigOptionBool*>(opt_base)->value = !no;
if (value.empty())
static_cast<ConfigOptionBool*>(opt_base)->value = !no;
else
opt_base->deserialize(value);
} 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;

View file

@ -2860,9 +2860,10 @@ namespace Slic3r {
stream << prefix << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
stream << prefix << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
}
assert(! volume->source.is_converted_from_inches || ! volume->source.is_converted_from_meters);
if (volume->source.is_converted_from_inches)
stream << prefix << SOURCE_IN_INCHES << "\" " << VALUE_ATTR << "=\"1\"/>\n";
if (volume->source.is_converted_from_meters)
else if (volume->source.is_converted_from_meters)
stream << prefix << SOURCE_IN_METERS << "\" " << VALUE_ATTR << "=\"1\"/>\n";
}

View file

@ -1241,9 +1241,10 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
stream << " <metadata type=\"slic3r.source_offset_y\">" << volume->source.mesh_offset(1) << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_offset_z\">" << volume->source.mesh_offset(2) << "</metadata>\n";
}
assert(! volume->source.is_converted_from_inches || ! volume->source.is_converted_from_meters);
if (volume->source.is_converted_from_inches)
stream << " <metadata type=\"slic3r.source_in_inches\">1</metadata>\n";
if (volume->source.is_converted_from_meters)
else if (volume->source.is_converted_from_meters)
stream << " <metadata type=\"slic3r.source_in_meters\">1</metadata>\n";
stream << std::setprecision(std::numeric_limits<float>::max_digits10);
const indexed_triangle_set &its = volume->mesh().its;

View file

@ -460,13 +460,15 @@ void Model::convert_multipart_object(unsigned int max_extruders)
this->objects.push_back(object);
}
static constexpr const double volume_threshold_inches = 9.0; // 9 = 3*3*3;
bool Model::looks_like_imperial_units() const
{
if (this->objects.size() == 0)
return false;
for (ModelObject* obj : this->objects)
if (obj->get_object_stl_stats().volume < 9.0) // 9 = 3*3*3;
if (obj->get_object_stl_stats().volume < volume_threshold_inches)
return true;
return false;
@ -474,22 +476,26 @@ bool Model::looks_like_imperial_units() const
void Model::convert_from_imperial_units(bool only_small_volumes)
{
double in_to_mm = 25.4;
static constexpr const in_to_mm = 25.4;
for (ModelObject* obj : this->objects)
if (! only_small_volumes || obj->get_object_stl_stats().volume < 9.0) { // 9 = 3*3*3;
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_inches) {
obj->scale_mesh_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
for (ModelVolume* v : obj->volumes)
for (ModelVolume* v : obj->volumes) {
assert(! v->source.is_converted_from_meters);
v->source.is_converted_from_inches = true;
}
}
}
static constexpr const double volume_threshold_meters = 0.001; // 0.001 = 0.1*0.1*0.1
bool Model::looks_like_saved_in_meters() const
{
if (this->objects.size() == 0)
return false;
for (ModelObject* obj : this->objects)
if (obj->get_object_stl_stats().volume < 0.001) // 0.001 = 0.1*0.1*0.1;
if (obj->get_object_stl_stats().volume < volume_threshold_meters)
return true;
return false;
@ -497,12 +503,14 @@ bool Model::looks_like_saved_in_meters() const
void Model::convert_from_meters(bool only_small_volumes)
{
double m_to_mm = 1000;
static constexpr const double m_to_mm = 1000;
for (ModelObject* obj : this->objects)
if (! only_small_volumes || obj->get_object_stl_stats().volume < 0.001) { // 0.001 = 0.1*0.1*0.1;
if (! only_small_volumes || obj->get_object_stl_stats().volume < volume_threshold_meters) {
obj->scale_mesh_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
for (ModelVolume* v : obj->volumes)
for (ModelVolume* v : obj->volumes) {
assert(! v->source.is_converted_from_inches);
v->source.is_converted_from_meters = true;
}
}
}
@ -725,6 +733,11 @@ void ModelObject::clear_volumes()
this->invalidate_bounding_box();
}
bool ModelObject::is_mm_painted() const
{
return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
}
void ModelObject::sort_volumes(bool full_sort)
{
// sort volumes inside the object to order "Model Part, Negative Volume, Modifier, Support Blocker and Support Enforcer. "
@ -1070,6 +1083,7 @@ void ModelObject::convert_units(ModelObjectPtrs& new_objects, ConversionType con
vol->source.is_converted_from_inches = conv_type == ConversionType::CONV_FROM_INCH;
if (conv_type == ConversionType::CONV_FROM_METER || conv_type == ConversionType::CONV_TO_METER)
vol->source.is_converted_from_meters = conv_type == ConversionType::CONV_FROM_METER;
assert(! vol->source.is_converted_from_inches || ! vol->source.is_converted_from_meters);
}
else
vol->set_offset(volume->get_offset());
@ -1740,18 +1754,15 @@ void ModelVolume::scale(const Vec3d& scaling_factors)
void ModelObject::scale_to_fit(const Vec3d &size)
{
/*
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
Vec3d orig_size = this->bounding_box().size();
float factor = fminf(
size.x / orig_size.x,
fminf(
size.y / orig_size.y,
size.z / orig_size.z
double factor = std::min(
size.x() / orig_size.x(),
std::min(
size.y() / orig_size.y(),
size.z() / orig_size.z()
)
);
this->scale(factor);
*/
}
void ModelVolume::assign_new_unique_ids_recursive()
@ -1825,6 +1836,7 @@ void ModelVolume::transform_this_mesh(const Matrix3d &matrix, bool fix_left_hand
void ModelVolume::convert_from_imperial_units()
{
assert(! this->source.is_converted_from_meters);
double in_to_mm = 25.4;
this->scale_geometry_after_creation(Vec3d(in_to_mm, in_to_mm, in_to_mm));
this->set_offset(Vec3d(0, 0, 0));
@ -1833,6 +1845,7 @@ void ModelVolume::convert_from_imperial_units()
void ModelVolume::convert_from_meters()
{
assert(! this->source.is_converted_from_inches);
double m_to_mm = 1000;
this->scale_geometry_after_creation(Vec3d(m_to_mm, m_to_mm, m_to_mm));
this->set_offset(Vec3d(0, 0, 0));

View file

@ -285,6 +285,8 @@ public:
void clear_volumes();
void sort_volumes(bool full_sort);
bool is_multiparts() const { return volumes.size() > 1; }
// Checks if any of object volume is painted using the multi-material painting gizmo.
bool is_mm_painted() const;
ModelInstance* add_instance();
ModelInstance* add_instance(const ModelInstance &instance);
@ -715,6 +717,8 @@ public:
this->mmu_segmentation_facets.set_new_unique_id();
}
bool is_mm_painted() const { return !this->mmu_segmentation_facets.empty(); }
protected:
friend class Print;
friend class SLAPrint;

View file

@ -5,6 +5,7 @@
#include "Print.hpp"
#include "VoronoiVisualUtils.hpp"
#include "MutablePolygon.hpp"
#include "format.hpp"
#include <utility>
#include <cfloat>
@ -502,11 +503,13 @@ static std::vector<std::vector<ColoredLine>> colorize_polygons(const std::vector
using boost::polygon::voronoi_diagram;
static inline Point mk_point(const Voronoi::VD::vertex_type *point) { return Point(coord_t(point->x()), coord_t(point->y())); }
static inline Point mk_point(const Voronoi::VD::vertex_type *point) { return {coord_t(point->x()), coord_t(point->y())}; }
static inline Point mk_point(const Voronoi::Internal::point_type &point) { return Point(coord_t(point.x()), coord_t(point.y())); }
static inline Point mk_point(const Voronoi::Internal::point_type &point) { return {coord_t(point.x()), coord_t(point.y())}; }
static inline Point mk_point(const voronoi_diagram<double>::vertex_type &point) { return Point(coord_t(point.x()), coord_t(point.y())); }
static inline Point mk_point(const voronoi_diagram<double>::vertex_type &point) { return {coord_t(point.x()), coord_t(point.y())}; }
static inline Vec2d mk_vec2(const voronoi_diagram<double>::vertex_type *point) { return {point->x(), point->y()}; }
struct MMU_Graph
{
@ -696,9 +699,9 @@ struct MMU_Graph
assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index());
vertex.color(this->get_border_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx);
} else if (bbox.contains(vertex_point)) {
if (auto [contour_pt, c_dist_sqr] = closest_contour_point.find(vertex_point); contour_pt != nullptr && c_dist_sqr < 3 * SCALED_EPSILON) {
if (auto [contour_pt, c_dist_sqr] = closest_contour_point.find(vertex_point); contour_pt != nullptr && c_dist_sqr < Slic3r::sqr(3 * SCALED_EPSILON)) {
vertex.color(this->get_global_index(contour_pt->m_contour_idx, contour_pt->m_point_idx));
} else if (auto [voronoi_pt, v_dist_sqr] = closest_voronoi_point.find(vertex_point); voronoi_pt == nullptr || v_dist_sqr >= 3 * SCALED_EPSILON) {
} else if (auto [voronoi_pt, v_dist_sqr] = closest_voronoi_point.find(vertex_point); voronoi_pt == nullptr || v_dist_sqr >= Slic3r::sqr(3 * SCALED_EPSILON)) {
closest_voronoi_point.insert(CPoint(vertex_point, this->nodes_count()));
vertex.color(this->nodes_count());
this->nodes.push_back({vertex_point});
@ -796,6 +799,27 @@ static inline void init_polygon_indices(const MMU_Graph
}
}
// Voronoi edges produced by Voronoi generator cloud have coordinates that don't fit inside coord_t (int32_t).
// Because of that, this function tries to clip edges that have one endpoint of the edge inside the BoundingBox.
static inline Line clip_finite_voronoi_edge(const Voronoi::VD::edge_type &edge, const BoundingBoxf &bbox)
{
assert(edge.is_finite());
Vec2d v0 = mk_vec2(edge.vertex0());
Vec2d v1 = mk_vec2(edge.vertex1());
bool contains_v0 = bbox.contains(v0);
bool contains_v1 = bbox.contains(v1);
if ((contains_v0 && contains_v1) || (!contains_v0 && !contains_v1))
return {mk_point(edge.vertex0()), mk_point(edge.vertex1())};
Vec2d vector = (v1 - v0).normalized() * bbox.size().norm();
if (!contains_v0)
v0 = (v1 - vector);
else
v1 = (v0 + vector);
return {v0.cast<coord_t>(), v1.cast<coord_t>()};
}
static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<ColoredLine>> &color_poly)
{
Geometry::VoronoiDiagram vd;
@ -852,7 +876,8 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
};
bbox.offset(scale_(10.));
const double bbox_dim_max = double(std::max(bbox.size().x(), bbox.size().y()));
const BoundingBoxf bbox_clip(bbox.min.cast<double>(), bbox.max.cast<double>());
const double bbox_dim_max = double(std::max(bbox.size().x(), bbox.size().y()));
// Make a copy of the input segments with the double type.
std::vector<Voronoi::Internal::segment_type> segments;
@ -890,72 +915,74 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
}
}
} else if (edge_it->is_finite()) {
const Point v0 = mk_point(edge_it->vertex0());
const Point v1 = mk_point(edge_it->vertex1());
const size_t from_idx = edge_it->vertex0()->color();
const size_t to_idx = edge_it->vertex1()->color();
// Both points are on contour, so skip them. In cases of duplicate Voronoi vertices, skip edges between the same two points.
if (graph.is_edge_connecting_two_contour_vertices(edge_it) || (edge_it->vertex0()->color() == edge_it->vertex1()->color())) continue;
if (graph.is_edge_connecting_two_contour_vertices(edge_it) || (edge_it->vertex0()->color() == edge_it->vertex1()->color()))
continue;
const Line edge_line(v0, v1);
const Line edge_line = clip_finite_voronoi_edge(*edge_it, bbox_clip);
const Line contour_line = lines_colored[edge_it->cell()->source_index()].line;
const ColoredLine colored_line = lines_colored[edge_it->cell()->source_index()];
const ColoredLine contour_line_prev = get_prev_contour_line(edge_it);
const ColoredLine contour_line_next = get_next_contour_line(edge_it);
Point intersection;
if (edge_it->vertex0()->color() >= graph.nodes_count() || edge_it->vertex1()->color() >= graph.nodes_count()) {
// if(edge_it->vertex0()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex0())) {
//
// }
if (edge_it->vertex1()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex1())) {
Line contour_line_twin = lines_colored[edge_it->twin()->cell()->source_index()].line;
enum class Vertex { VERTEX0, VERTEX1 };
auto append_edge_if_intersects_with_contour = [&graph, &lines_colored, &edge_line, &contour_line](const voronoi_diagram<double>::const_edge_iterator &edge_iterator, const Vertex vertex) {
Point intersection;
Line contour_line_twin = lines_colored[edge_iterator->twin()->cell()->source_index()].line;
if (line_intersection_with_epsilon(contour_line_twin, edge_line, &intersection)) {
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_it->twin()->cell()->source_index());
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_iterator->twin()->cell()->source_index());
const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line_twin, intersection) ? graph_arc.from_idx :
graph_arc.to_idx;
graph.append_edge(edge_it->vertex1()->color(), to_idx_l);
graph.append_edge(vertex == Vertex::VERTEX0 ? edge_iterator->vertex0()->color() : edge_iterator->vertex1()->color(), to_idx_l);
} else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) {
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_it->cell()->source_index());
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_iterator->cell()->source_index());
const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line, intersection) ? graph_arc.from_idx : graph_arc.to_idx;
graph.append_edge(edge_it->vertex1()->color(), to_idx_l);
graph.append_edge(vertex == Vertex::VERTEX0 ? edge_iterator->vertex0()->color() : edge_iterator->vertex1()->color(), to_idx_l);
}
mark_processed(edge_it);
}
mark_processed(edge_iterator);
};
if (edge_it->vertex0()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex0()))
append_edge_if_intersects_with_contour(edge_it, Vertex::VERTEX0);
if (edge_it->vertex1()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex1()))
append_edge_if_intersects_with_contour(edge_it, Vertex::VERTEX1);
} else if (graph.is_edge_attach_to_contour(edge_it)) {
mark_processed(edge_it);
// Skip edges witch connection two points on a contour
if (graph.is_edge_connecting_two_contour_vertices(edge_it))
continue;
const size_t from_idx = edge_it->vertex0()->color();
const size_t to_idx = edge_it->vertex1()->color();
if (graph.is_vertex_on_contour(edge_it->vertex0())) {
if (is_point_closer_to_beginning_of_line(contour_line, v0)) {
if ((!has_same_color(contour_line_prev, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line_prev.line, contour_line, v1)) {
if (is_point_closer_to_beginning_of_line(contour_line, edge_line.a)) {
if ((!has_same_color(contour_line_prev, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line_prev.line, contour_line, edge_line.b)) {
graph.append_edge(from_idx, to_idx);
force_edge_adding[colored_line.poly_idx] = false;
}
} else {
if ((!has_same_color(contour_line_next, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line, contour_line_next.line, v1)) {
if ((!has_same_color(contour_line_next, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line, contour_line_next.line, edge_line.b)) {
graph.append_edge(from_idx, to_idx);
force_edge_adding[colored_line.poly_idx] = false;
}
}
} else {
assert(graph.is_vertex_on_contour(edge_it->vertex1()));
if (is_point_closer_to_beginning_of_line(contour_line, v1)) {
if ((!has_same_color(contour_line_prev, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line_prev.line, contour_line, v0)) {
if (is_point_closer_to_beginning_of_line(contour_line, edge_line.b)) {
if ((!has_same_color(contour_line_prev, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line_prev.line, contour_line, edge_line.a)) {
graph.append_edge(from_idx, to_idx);
force_edge_adding[colored_line.poly_idx] = false;
}
} else {
if ((!has_same_color(contour_line_next, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line, contour_line_next.line, v0)) {
if ((!has_same_color(contour_line_next, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line, contour_line_next.line, edge_line.a)) {
graph.append_edge(from_idx, to_idx);
force_edge_adding[colored_line.poly_idx] = false;
}
}
}
} else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) {
} else if (Point intersection; line_intersection_with_epsilon(contour_line, edge_line, &intersection)) {
mark_processed(edge_it);
Point real_v0 = graph.nodes[edge_it->vertex0()->color()].point;
Point real_v1 = graph.nodes[edge_it->vertex1()->color()].point;
@ -1202,7 +1229,7 @@ static void cut_segmented_layers(const std::vector<ExPolygons>
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - cutting segmented layers in parallel - end";
}
// #define MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
//#define MMU_SEGMENTATION_DEBUG_TOP_BOTTOM
// Returns MMU segmentation of top and bottom layers based on painting in MMU segmentation gizmo
static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bottom_layers(const PrintObject &print_object,
@ -1671,7 +1698,7 @@ static void export_regions_to_svg(const std::string &path, const std::vector<std
svg.draw_outline(lslices, "green", "lime", stroke_width);
for (const std::pair<ExPolygon, size_t> &region : regions) {
int region_color = region.second;
int region_color = int(region.second);
if (region_color >= 0 && region_color < int(colors.size()))
svg.draw(region.first, colors[region_color]);
else

View file

@ -3,6 +3,12 @@
#include <boost/log/trivial.hpp>
#include <boost/filesystem/operations.hpp>
#if defined(__APPLE__)
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/machine.h>
#endif
namespace Slic3r {
static auto s_platform = Platform::Uninitialized;
@ -16,8 +22,39 @@ void detect_platform()
s_platform_flavor = PlatformFlavor::Generic;
#elif defined(__APPLE__)
BOOST_LOG_TRIVIAL(info) << "Platform: OSX";
s_platform = Platform::OSX;
s_platform_flavor = PlatformFlavor::Generic;
s_platform = Platform::OSX;
s_platform_flavor = PlatformFlavor::GenericOSX;
{
cpu_type_t type = 0;
size_t size = sizeof(type);
if (sysctlbyname("hw.cputype", &type, &size, NULL, 0) == 0) {
type &= ~CPU_ARCH_MASK;
if (type == CPU_TYPE_X86) {
int proc_translated = 0;
size = sizeof(proc_translated);
// Detect if native CPU is really X86 or PrusaSlicer runs through Rosetta.
if (sysctlbyname("sysctl.proc_translated", &proc_translated, &size, NULL, 0) == -1) {
if (errno == ENOENT) {
// Native CPU is X86, and property sysctl.proc_translated doesn't exist.
s_platform_flavor = PlatformFlavor::OSXOnX86;
BOOST_LOG_TRIVIAL(info) << "Platform flavor: OSXOnX86";
}
} else if (proc_translated == 1) {
// Native CPU is ARM and PrusaSlicer runs through Rosetta.
s_platform_flavor = PlatformFlavor::OSXOnArm;
BOOST_LOG_TRIVIAL(info) << "Platform flavor: OSXOnArm";
} else {
// Native CPU is X86.
s_platform_flavor = PlatformFlavor::OSXOnX86;
BOOST_LOG_TRIVIAL(info) << "Platform flavor: OSXOnX86";
}
} else if (type == CPU_TYPE_ARM) {
// Native CPU is ARM
s_platform_flavor = PlatformFlavor::OSXOnArm;
BOOST_LOG_TRIVIAL(info) << "Platform flavor: OSXOnArm";
}
}
}
#elif defined(__linux__)
BOOST_LOG_TRIVIAL(info) << "Platform: Linux";
s_platform = Platform::Linux;

View file

@ -28,6 +28,12 @@ enum class PlatformFlavor
WSL2,
// For Platform::BSDUnix
OpenBSD,
// For Platform::OSX
GenericOSX,
// For Apple's on Intel X86 CPU
OSXOnX86,
// For Apple's on Arm CPU
OSXOnArm,
};
// To be called on program start-up.

View file

@ -317,6 +317,8 @@ public:
bool has_support() const { return m_config.support_material || m_config.support_material_enforce_layers > 0; }
bool has_raft() const { return m_config.raft_layers > 0; }
bool has_support_material() const { return this->has_support() || this->has_raft(); }
// Checks if the model object is painted using the multi-material painting gizmo.
bool is_mm_painted() const { return this->model_object()->is_mm_painted(); };
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
std::vector<unsigned int> object_extruders() const;

View file

@ -812,7 +812,8 @@ static PrintObjectRegions* generate_print_object_regions(
layer_ranges_regions.push_back({ range.layer_height_range, range.config });
}
update_volume_bboxes(layer_ranges_regions, out->cached_volume_ids, model_volumes, out->trafo_bboxes, std::max(0.f, xy_size_compensation));
const bool is_mm_painted = std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
update_volume_bboxes(layer_ranges_regions, out->cached_volume_ids, model_volumes, out->trafo_bboxes, is_mm_painted ? 0.f : std::max(0.f, xy_size_compensation));
std::vector<PrintRegion*> region_set;
auto get_create_region = [&region_set, &all_regions](PrintRegionConfig &&config) -> PrintRegion* {
@ -1313,7 +1314,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
m_default_region_config,
model_object_status.print_instances.front().trafo,
num_extruders,
float(print_object.config().xy_size_compensation.value),
print_object.is_mm_painted() ? 0.f : float(print_object.config().xy_size_compensation.value),
painting_extruders);
}
for (auto it = it_print_object; it != it_print_object_end; ++it)

View file

@ -4154,6 +4154,11 @@ CLITransformConfigDef::CLITransformConfigDef()
def->label = L("Don't arrange");
def->tooltip = L("Do not rearrange the given models before merging and keep their original XY coordinates.");
def = this->add("ensure_on_bed", coBool);
def->label = L("Ensure on bed");
def->tooltip = L("Lift the object above the bed when it is partially below. Enabled by default, use --no-ensure-on-bed to disable.");
def->set_default_value(new ConfigOptionBool(true));
def = this->add("duplicate", coInt);
def->label = L("Duplicate");
def->tooltip =L("Multiply copies by this factor.");

View file

@ -509,13 +509,32 @@ bool PrintObject::invalidate_state_by_config_options(
} else if (
opt_key == "perimeters"
|| opt_key == "extra_perimeters"
|| opt_key == "gap_fill_enabled"
|| opt_key == "gap_fill_speed"
|| opt_key == "first_layer_extrusion_width"
|| opt_key == "perimeter_extrusion_width"
|| opt_key == "infill_overlap"
|| opt_key == "external_perimeters_first") {
steps.emplace_back(posPerimeters);
} else if (
opt_key == "gap_fill_enabled"
|| opt_key == "gap_fill_speed") {
// Return true if gap-fill speed has changed from zero value to non-zero or from non-zero value to zero.
auto is_gap_fill_changed_state_due_to_speed = [&opt_key, &old_config, &new_config]() -> bool {
if (opt_key == "gap_fill_speed") {
const auto *old_gap_fill_speed = old_config.option<ConfigOptionFloat>(opt_key);
const auto *new_gap_fill_speed = new_config.option<ConfigOptionFloat>(opt_key);
assert(old_gap_fill_speed && new_gap_fill_speed);
return (old_gap_fill_speed->value > 0.f && new_gap_fill_speed->value == 0.f) ||
(old_gap_fill_speed->value == 0.f && new_gap_fill_speed->value > 0.f);
}
return false;
};
// Filtering of unprintable regions in multi-material segmentation depends on if gap-fill is enabled or not.
// So step posSlice is invalidated when gap-fill was enabled/disabled by option "gap_fill_enabled" or by
// changing "gap_fill_speed" to force recomputation of the multi-material segmentation.
if (this->is_mm_painted() && (opt_key == "gap_fill_enabled" || (opt_key == "gap_fill_speed" && is_gap_fill_changed_state_due_to_speed())))
steps.emplace_back(posSlice);
steps.emplace_back(posPerimeters);
} else if (
opt_key == "layer_height"
|| opt_key == "first_layer_height"

View file

@ -167,7 +167,8 @@ static std::vector<VolumeSlices> slice_volumes_inner(
params_base.mode_below = params_base.mode;
const auto extra_offset = std::max(0.f, float(print_object_config.xy_size_compensation.value));
const bool is_mm_painted = std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
const auto extra_offset = is_mm_painted ? 0.f : std::max(0.f, float(print_object_config.xy_size_compensation.value));
for (const ModelVolume *model_volume : model_volumes)
if (model_volume_needs_slicing(*model_volume)) {
@ -725,6 +726,17 @@ void PrintObject::slice_volumes()
// Is any ModelVolume MMU painted?
if (const auto& volumes = this->model_object()->volumes;
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume* v) { return !v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
// If XY Size compensation is also enabled, notify the user that XY Size compensation
// would not be used because the object is multi-material painted.
if (m_config.xy_size_compensation.value != 0.f) {
this->active_step_add_warning(
PrintStateBase::WarningLevel::CRITICAL,
L("An object has enabled XY Size compensation which will not be used because it is also multi-material painted.\nXY Size "
"compensation cannot be combined with multi-material painting.") +
"\n" + (L("Object name")) + ": " + this->model_object()->name);
}
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - MMU segmentation";
apply_mm_segmentation(*this, [print]() { print->throw_if_canceled(); });
}
@ -733,8 +745,8 @@ void PrintObject::slice_volumes()
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin";
{
// Compensation value, scaled. Only applying the negative scaling here, as the positive scaling has already been applied during slicing.
const auto xy_compensation_scaled = scaled<float>(std::min(m_config.xy_size_compensation.value, 0.));
const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ?
const auto xy_compensation_scaled = this->is_mm_painted() ? scaled<float>(0.f) : scaled<float>(std::min(m_config.xy_size_compensation.value, 0.));
const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ?
// Only enable Elephant foot compensation if printing directly on the print bed.
float(scale_(m_config.elefant_foot_compensation.value)) :
0.f;

View file

@ -372,7 +372,7 @@ static inline IntersectionLines slice_make_lines(
FaceFilter face_filter)
{
IntersectionLines lines;
for (int face_idx = 0; face_idx < mesh_faces.size(); ++ face_idx)
for (int face_idx = 0; face_idx < int(mesh_faces.size()); ++ face_idx)
if (face_filter(face_idx)) {
const Vec3i &indices = mesh_faces[face_idx];
stl_vertex vertices[3] { transform_vertex_fn(mesh_vertices[indices(0)]), transform_vertex_fn(mesh_vertices[indices(1)]), transform_vertex_fn(mesh_vertices[indices(2)]) };

View file

@ -277,22 +277,22 @@ void TriangleSelector::append_touching_subtriangles(int itriangle, int vertexi,
if (itriangle == -1)
return;
auto process_subtriangle = [this, &itriangle, &vertexi, &vertexj, &touching_subtriangles_out](const int subtriangle_idx) -> void {
auto process_subtriangle = [this, &itriangle, &vertexi, &vertexj, &touching_subtriangles_out](const int subtriangle_idx, Partition partition) -> void {
assert(subtriangle_idx == -1);
if (!m_triangles[subtriangle_idx].is_split())
touching_subtriangles_out.emplace_back(subtriangle_idx);
else if (int midpoint = this->triangle_midpoint(itriangle, vertexi, vertexj); midpoint != -1)
append_touching_subtriangles(subtriangle_idx, vertexi, midpoint, touching_subtriangles_out);
append_touching_subtriangles(subtriangle_idx, partition == Partition::First ? vertexi : midpoint, partition == Partition::First ? midpoint : vertexj, touching_subtriangles_out);
else
append_touching_subtriangles(subtriangle_idx, vertexi, vertexj, touching_subtriangles_out);
};
std::pair<int, int> touching = this->triangle_subtriangles(itriangle, vertexi, vertexj);
if (touching.first != -1)
process_subtriangle(touching.first);
process_subtriangle(touching.first, Partition::First);
if (touching.second != -1)
process_subtriangle(touching.second);
process_subtriangle(touching.second, Partition::Second);
}
void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate)
@ -437,7 +437,7 @@ int TriangleSelector::neighbor_child(int itriangle, int vertexi, int vertexj, Pa
std::pair<int, int> TriangleSelector::triangle_subtriangles(int itriangle, int vertexi, int vertexj) const
{
return itriangle == -1 ? std::make_pair(-1, -1) : this->triangle_subtriangles(m_triangles[itriangle], vertexi, vertexj);
return itriangle == -1 ? std::make_pair(-1, -1) : Slic3r::TriangleSelector::triangle_subtriangles(m_triangles[itriangle], vertexi, vertexj);
}
std::pair<int, int> TriangleSelector::triangle_subtriangles(const Triangle &tr, int vertexi, int vertexj)

View file

@ -100,8 +100,8 @@ extern bool is_ini_file(const boost::filesystem::directory_entry &path);
extern bool is_idx_file(const boost::filesystem::directory_entry &path);
extern bool is_gcode_file(const std::string &path);
extern bool is_img_file(const std::string& path);
extern bool is_stl_file(const boost::filesystem::directory_entry& path);
extern bool is_stl_file(const std::string& path);
extern bool is_gallery_file(const boost::filesystem::directory_entry& path, char const* type);
extern bool is_gallery_file(const std::string& path, char const* type);
extern bool is_shapes_dir(const std::string& dir);
// File path / name / extension splitting utilities, working with UTF-8,

View file

@ -766,14 +766,14 @@ bool is_img_file(const std::string &path)
return boost::iends_with(path, ".png") || boost::iends_with(path, ".svg");
}
bool is_stl_file(const boost::filesystem::directory_entry& dir_entry)
bool is_gallery_file(const boost::filesystem::directory_entry& dir_entry, char const* type)
{
return is_plain_file(dir_entry) && strcasecmp(dir_entry.path().extension().string().c_str(), ".stl") == 0;
return is_plain_file(dir_entry) && strcasecmp(dir_entry.path().extension().string().c_str(), type) == 0;
}
bool is_stl_file(const std::string &path)
bool is_gallery_file(const std::string &path, char const* type)
{
return boost::iends_with(path, ".stl");
return boost::iends_with(path, type);
}
bool is_shapes_dir(const std::string& dir)