Merge branch 'master' into fs_QuadricEdgeCollapse

This commit is contained in:
Filip Sykala 2021-08-18 10:37:21 +02:00
commit cc88b1e86b
18 changed files with 269 additions and 137 deletions

View File

@ -2,10 +2,10 @@
# Building PrusaSlicer on UNIX/Linux # Building PrusaSlicer on UNIX/Linux
PrusaSlicer uses the CMake build system and requires several dependencies. PrusaSlicer uses the CMake build system and requires several dependencies.
The dependencies can be listed in `deps/deps-linux.cmake` and `deps/deps-unix-common.cmake`, although they don't necessarily need to be as recent The dependencies can be listed in the `deps` directory in individual subdirectories, although they don't necessarily need to be as recent
as the versions listed - generally versions available on conservative Linux distros such as Debian stable or CentOS should suffice. as the versions listed - generally versions available on conservative Linux distros such as Debian stable, Ubuntu LTS releases or Fedora are likely sufficient.
Perl is not required any more. Perl is not required anymore.
In a typical situation, one would open a command line, go to the PrusaSlicer sources (**the root directory of the repository**), create a directory called `build` or similar, In a typical situation, one would open a command line, go to the PrusaSlicer sources (**the root directory of the repository**), create a directory called `build` or similar,
`cd` into it and call: `cd` into it and call:

View File

@ -45,16 +45,38 @@ static float max_brim_width(const ConstPrintObjectPtrsAdaptor &objects)
})); }));
} }
static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print) // Returns ExPolygons of the bottom layer of the print object after elephant foot compensation.
static ExPolygons get_print_object_bottom_layer_expolygons(const PrintObject &print_object)
{ {
ExPolygons ex_polygons;
for (LayerRegion *region : print_object.layers().front()->regions())
Slic3r::append(ex_polygons, offset_ex(offset_ex(region->slices.surfaces, float(SCALED_EPSILON)), -float(SCALED_EPSILON)));
return ex_polygons;
}
// Returns ExPolygons of bottom layer for every print object in Print after elephant foot compensation.
static std::vector<ExPolygons> get_print_bottom_layers_expolygons(const Print &print)
{
std::vector<ExPolygons> bottom_layers_expolygons;
bottom_layers_expolygons.reserve(print.objects().size());
for (const PrintObject *object : print.objects())
bottom_layers_expolygons.emplace_back(get_print_object_bottom_layer_expolygons(*object));
return bottom_layers_expolygons;
}
static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print, const std::vector<ExPolygons> &bottom_layers_expolygons)
{
assert(print.objects().size() == bottom_layers_expolygons.size());
Polygons islands; Polygons islands;
ConstPrintObjectPtrs island_to_object; ConstPrintObjectPtrs island_to_object;
for (const PrintObject *object : print.objects()) { for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
Polygons islands_object; Polygons islands_object;
islands_object.reserve(object->layers().front()->lslices.size()); islands_object.reserve(bottom_layers_expolygons[print_object_idx].size());
for (const ExPolygon &ex_poly : object->layers().front()->lslices) for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx])
islands_object.emplace_back(ex_poly.contour); islands_object.emplace_back(ex_poly.contour);
const PrintObject *object = print.objects()[print_object_idx];
islands.reserve(islands.size() + object->instances().size() * islands_object.size()); islands.reserve(islands.size() + object->instances().size() * islands_object.size());
for (const PrintInstance &instance : object->instances()) for (const PrintInstance &instance : object->instances())
for (Polygon &poly : islands_object) { for (Polygon &poly : islands_object) {
@ -110,7 +132,7 @@ static Polygons top_level_outer_brim_islands(const ConstPrintObjectPtrs &top_lev
//FIXME how about the brim type? //FIXME how about the brim type?
auto brim_offset = float(scale_(object->config().brim_offset.value)); auto brim_offset = float(scale_(object->config().brim_offset.value));
Polygons islands_object; Polygons islands_object;
for (const ExPolygon &ex_poly : object->layers().front()->lslices) { for (const ExPolygon &ex_poly : get_print_object_bottom_layer_expolygons(*object)) {
Polygons contour_offset = offset(ex_poly.contour, brim_offset); Polygons contour_offset = offset(ex_poly.contour, brim_offset);
for (Polygon &poly : contour_offset) for (Polygon &poly : contour_offset)
poly.douglas_peucker(SCALED_RESOLUTION); poly.douglas_peucker(SCALED_RESOLUTION);
@ -124,8 +146,12 @@ static Polygons top_level_outer_brim_islands(const ConstPrintObjectPtrs &top_lev
return islands; return islands;
} }
static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, const float no_brim_offset) static ExPolygons top_level_outer_brim_area(const Print &print,
const ConstPrintObjectPtrs &top_level_objects_with_brim,
const std::vector<ExPolygons> &bottom_layers_expolygons,
const float no_brim_offset)
{ {
assert(print.objects().size() == bottom_layers_expolygons.size());
std::unordered_set<size_t> top_level_objects_idx; std::unordered_set<size_t> top_level_objects_idx;
top_level_objects_idx.reserve(top_level_objects_with_brim.size()); top_level_objects_idx.reserve(top_level_objects_with_brim.size());
for (const PrintObject *object : top_level_objects_with_brim) for (const PrintObject *object : top_level_objects_with_brim)
@ -133,15 +159,16 @@ static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrint
ExPolygons brim_area; ExPolygons brim_area;
ExPolygons no_brim_area; ExPolygons no_brim_area;
for (const PrintObject *object : print.objects()) { for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
const BrimType brim_type = object->config().brim_type.value; const PrintObject *object = print.objects()[print_object_idx];
const float brim_offset = scale_(object->config().brim_offset.value); const BrimType brim_type = object->config().brim_type.value;
const float brim_width = scale_(object->config().brim_width.value); const float brim_offset = scale_(object->config().brim_offset.value);
const bool is_top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end(); const float brim_width = scale_(object->config().brim_width.value);
const bool is_top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
ExPolygons brim_area_object; ExPolygons brim_area_object;
ExPolygons no_brim_area_object; ExPolygons no_brim_area_object;
for (const ExPolygon &ex_poly : object->layers().front()->lslices) { for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) {
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim) if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim)
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset))); append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
@ -166,8 +193,12 @@ static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrint
return diff_ex(brim_area, no_brim_area); return diff_ex(brim_area, no_brim_area);
} }
static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, const float no_brim_offset) static ExPolygons inner_brim_area(const Print &print,
const ConstPrintObjectPtrs &top_level_objects_with_brim,
const std::vector<ExPolygons> &bottom_layers_expolygons,
const float no_brim_offset)
{ {
assert(print.objects().size() == bottom_layers_expolygons.size());
std::unordered_set<size_t> top_level_objects_idx; std::unordered_set<size_t> top_level_objects_idx;
top_level_objects_idx.reserve(top_level_objects_with_brim.size()); top_level_objects_idx.reserve(top_level_objects_with_brim.size());
for (const PrintObject *object : top_level_objects_with_brim) for (const PrintObject *object : top_level_objects_with_brim)
@ -176,16 +207,17 @@ static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs
ExPolygons brim_area; ExPolygons brim_area;
ExPolygons no_brim_area; ExPolygons no_brim_area;
Polygons holes; Polygons holes;
for (const PrintObject *object : print.objects()) { for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
const BrimType brim_type = object->config().brim_type.value; const PrintObject *object = print.objects()[print_object_idx];
const float brim_offset = scale_(object->config().brim_offset.value); const BrimType brim_type = object->config().brim_type.value;
const float brim_width = scale_(object->config().brim_width.value); const float brim_offset = scale_(object->config().brim_offset.value);
const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end(); const float brim_width = scale_(object->config().brim_width.value);
const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
ExPolygons brim_area_object; ExPolygons brim_area_object;
ExPolygons no_brim_area_object; ExPolygons no_brim_area_object;
Polygons holes_object; Polygons holes_object;
for (const ExPolygon &ex_poly : object->layers().front()->lslices) { for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) {
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) { if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) {
if (top_outer_brim) if (top_outer_brim)
no_brim_area_object.emplace_back(ex_poly); no_brim_area_object.emplace_back(ex_poly);
@ -204,7 +236,7 @@ static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs
append(holes_object, ex_poly.holes); append(holes_object, ex_poly.holes);
} }
append(no_brim_area_object, offset_ex(object->layers().front()->lslices, brim_offset)); append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_offset));
for (const PrintInstance &instance : object->instances()) { for (const PrintInstance &instance : object->instances()) {
append_and_translate(brim_area, brim_area_object, instance); append_and_translate(brim_area, brim_area_object, instance);
@ -236,7 +268,7 @@ static void optimize_polylines_by_reversing(Polylines *polylines)
static Polylines connect_brim_lines(Polylines &&polylines, const Polygons &brim_area, float max_connection_length) static Polylines connect_brim_lines(Polylines &&polylines, const Polygons &brim_area, float max_connection_length)
{ {
if (polylines.empty()) if (polylines.empty())
return Polylines(); return {};
BoundingBox bbox = get_extents(polylines); BoundingBox bbox = get_extents(polylines);
bbox.merge(get_extents(brim_area)); bbox.merge(get_extents(brim_area));
@ -305,16 +337,20 @@ static Polylines connect_brim_lines(Polylines &&polylines, const Polygons &brim_
} }
} }
if (end < polylines.size()) if (end < polylines.size())
polylines.erase(polylines.begin() + end, polylines.end()); polylines.erase(polylines.begin() + int(end), polylines.end());
} }
return std::move(polylines); return std::move(polylines);
} }
static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, ExtrusionEntityCollection &brim) static void make_inner_brim(const Print &print,
const ConstPrintObjectPtrs &top_level_objects_with_brim,
const std::vector<ExPolygons> &bottom_layers_expolygons,
ExtrusionEntityCollection &brim)
{ {
assert(print.objects().size() == bottom_layers_expolygons.size());
Flow flow = print.brim_flow(); Flow flow = print.brim_flow();
ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, float(flow.scaled_spacing())); ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()));
Polygons loops; Polygons loops;
islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), jtSquare); islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), jtSquare);
for (size_t i = 0; !islands_ex.empty(); ++i) { for (size_t i = 0; !islands_ex.empty(); ++i) {
@ -334,11 +370,12 @@ static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_
// Collect islands_area to be merged into the final 1st layer convex hull. // Collect islands_area to be merged into the final 1st layer convex hull.
ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area) ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area)
{ {
Flow flow = print.brim_flow(); Flow flow = print.brim_flow();
ConstPrintObjectPtrs top_level_objects_with_brim = get_top_level_objects_with_brim(print); std::vector<ExPolygons> bottom_layers_expolygons = get_print_bottom_layers_expolygons(print);
Polygons islands = top_level_outer_brim_islands(top_level_objects_with_brim); ConstPrintObjectPtrs top_level_objects_with_brim = get_top_level_objects_with_brim(print, bottom_layers_expolygons);
ExPolygons islands_area_ex = top_level_outer_brim_area(print, top_level_objects_with_brim, float(flow.scaled_spacing())); Polygons islands = top_level_outer_brim_islands(top_level_objects_with_brim);
islands_area = to_polygons(islands_area_ex); ExPolygons islands_area_ex = top_level_outer_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()));
islands_area = to_polygons(islands_area_ex);
Polygons loops; Polygons loops;
size_t num_loops = size_t(floor(max_brim_width(print.objects()) / flow.spacing())); size_t num_loops = size_t(floor(max_brim_width(print.objects()) / flow.spacing()));
@ -536,7 +573,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
} }
make_inner_brim(print, top_level_objects_with_brim, brim); make_inner_brim(print, top_level_objects_with_brim, bottom_layers_expolygons, brim);
return brim; return brim;
} }

View File

@ -295,7 +295,7 @@ void PrintConfigDef::init_common_params()
def->sidetext = L("mm"); def->sidetext = L("mm");
def->min = 0; def->min = 0;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.2)); def->set_default_value(new ConfigOptionFloat(0.));
// Options used by physical printers // Options used by physical printers
@ -491,10 +491,11 @@ void PrintConfigDef::init_fff_params()
def = this->add("brim_offset", coFloat); def = this->add("brim_offset", coFloat);
def->label = L("Brim offset"); def->label = L("Brim offset");
def->category = L("Skirt and brim"); def->category = L("Skirt and brim");
def->tooltip = L("The offset of the brim from the printed object."); def->tooltip = L("The offset of the brim from the printed object. The offset is applied after the elephant foot compensation.");
def->sidetext = L("mm"); def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0)); def->set_default_value(new ConfigOptionFloat(0.f));
def = this->add("clip_multipart_objects", coBool); def = this->add("clip_multipart_objects", coBool);
def->label = L("Clip multi-part objects"); def->label = L("Clip multi-part objects");

View File

@ -58,29 +58,6 @@ T sum_score(AccessFn &&accessfn, size_t facecount, size_t Nthreads)
return execution::reduce(ex_tbb, from, to, initv, mergefn, accessfn, grainsize); return execution::reduce(ex_tbb, from, to, initv, mergefn, accessfn, grainsize);
} }
// Try to guess the number of support points needed to support a mesh
double get_misalginment_score(const TriangleMesh &mesh, const Transform3f &tr)
{
if (mesh.its.vertices.empty()) return std::nan("");
auto accessfn = [&mesh, &tr](size_t fi) {
auto triangle = get_transformed_triangle(mesh, tr, fi);
Vec3f U = triangle[1] - triangle[0];
Vec3f V = triangle[2] - triangle[0];
Vec3f C = U.cross(V);
// We should score against the alignment with the reference planes
return scaled<int_fast64_t>(std::abs(C.dot(Vec3f::UnitX())) +
std::abs(C.dot(Vec3f::UnitY())));
};
size_t facecount = mesh.its.indices.size();
size_t Nthreads = std::thread::hardware_concurrency();
double S = unscaled(sum_score<int_fast64_t>(accessfn, facecount, Nthreads));
return S / facecount;
}
// Get area and normal of a triangle // Get area and normal of a triangle
struct Facestats { struct Facestats {
Vec3f normal; Vec3f normal;
@ -96,21 +73,45 @@ struct Facestats {
} }
}; };
// Try to guess the number of support points needed to support a mesh
double get_misalginment_score(const TriangleMesh &mesh, const Transform3f &tr)
{
if (mesh.its.vertices.empty()) return std::nan("");
auto accessfn = [&mesh, &tr](size_t fi) {
Facestats fc{get_transformed_triangle(mesh, tr, fi)};
float score = fc.area
* (std::abs(fc.normal.dot(Vec3f::UnitX()))
+ std::abs(fc.normal.dot(Vec3f::UnitY()))
+ std::abs(fc.normal.dot(Vec3f::UnitZ())));
// We should score against the alignment with the reference planes
return scaled<int_fast64_t>(score);
};
size_t facecount = mesh.its.indices.size();
size_t Nthreads = std::thread::hardware_concurrency();
double S = unscaled(sum_score<int_fast64_t>(accessfn, facecount, Nthreads));
return S / facecount;
}
// The score function for a particular face // The score function for a particular face
inline double get_supportedness_score(const Facestats &fc) inline double get_supportedness_score(const Facestats &fc)
{ {
// Simply get the angle (acos of dot product) between the face normal and // Simply get the angle (acos of dot product) between the face normal and
// the DOWN vector. // the DOWN vector.
float phi = 1. - std::acos(fc.normal.dot(DOWN)) / float(PI); float cosphi = fc.normal.dot(DOWN);
float phi = 1.f - std::acos(cosphi) / float(PI);
// Only consider faces that have slopes below 90 deg:
phi = phi * (phi >= 0.5f);
// Make the huge slopes more significant than the smaller slopes // Make the huge slopes more significant than the smaller slopes
phi = phi * phi * phi; phi = phi * phi * phi;
// Multiply with the area of the current face // Multiply with the square root of face area of the current face,
return fc.area * POINTS_PER_UNIT_AREA * phi; // the area is less important as it grows.
// This makes many smaller overhangs a bigger impact.
return std::sqrt(fc.area) * POINTS_PER_UNIT_AREA * phi;
} }
// Try to guess the number of support points needed to support a mesh // Try to guess the number of support points needed to support a mesh
@ -120,8 +121,7 @@ double get_supportedness_score(const TriangleMesh &mesh, const Transform3f &tr)
auto accessfn = [&mesh, &tr](size_t fi) { auto accessfn = [&mesh, &tr](size_t fi) {
Facestats fc{get_transformed_triangle(mesh, tr, fi)}; Facestats fc{get_transformed_triangle(mesh, tr, fi)};
return scaled<int_fast64_t>(get_supportedness_score(fc));
return get_supportedness_score(fc);
}; };
size_t facecount = mesh.its.indices.size(); size_t facecount = mesh.its.indices.size();
@ -164,7 +164,7 @@ float get_supportedness_onfloor_score(const TriangleMesh &mesh,
Facestats fc{tri}; Facestats fc{tri};
if (tri[0].z() <= zlvl && tri[1].z() <= zlvl && tri[2].z() <= zlvl) if (tri[0].z() <= zlvl && tri[1].z() <= zlvl && tri[2].z() <= zlvl)
return -fc.area * POINTS_PER_UNIT_AREA; return -2 * fc.area * POINTS_PER_UNIT_AREA;
return get_supportedness_score(fc); return get_supportedness_score(fc);
}; };
@ -283,6 +283,26 @@ std::array<double, N> find_min_score(Fn &&fn, It from, It to, StopCond &&stopfn)
} // namespace } // namespace
// Assemble the mesh with the correct transformation to be used in rotation
// optimization.
TriangleMesh get_mesh_to_rotate(const ModelObject &mo)
{
TriangleMesh mesh = mo.raw_mesh();
mesh.require_shared_vertices();
ModelInstance *mi = mo.instances[0];
auto rotation = Vec3d::Zero();
auto offset = Vec3d::Zero();
Transform3d trafo_instance = Geometry::assemble_transform(offset,
rotation,
mi->get_scaling_factor(),
mi->get_mirror());
mesh.transform(trafo_instance);
return mesh;
}
Vec2d find_best_misalignment_rotation(const ModelObject & mo, Vec2d find_best_misalignment_rotation(const ModelObject & mo,
const RotOptimizeParams &params) const RotOptimizeParams &params)
{ {
@ -293,8 +313,7 @@ Vec2d find_best_misalignment_rotation(const ModelObject & mo,
// We will use only one instance of this converted mesh to examine different // We will use only one instance of this converted mesh to examine different
// rotations // rotations
TriangleMesh mesh = mo.raw_mesh(); TriangleMesh mesh = get_mesh_to_rotate(mo);
mesh.require_shared_vertices();
// To keep track of the number of iterations // To keep track of the number of iterations
int status = 0; int status = 0;
@ -326,7 +345,7 @@ Vec2d find_best_misalignment_rotation(const ModelObject & mo,
// We are searching rotations around only two axes x, y. Thus the // We are searching rotations around only two axes x, y. Thus the
// problem becomes a 2 dimensional optimization task. // problem becomes a 2 dimensional optimization task.
// We can specify the bounds for a dimension in the following way: // We can specify the bounds for a dimension in the following way:
auto bounds = opt::bounds({ {-PI/2, PI/2}, {-PI/2, PI/2} }); auto bounds = opt::bounds({ {-PI, PI}, {-PI, PI} });
auto result = solver.to_max().optimize( auto result = solver.to_max().optimize(
[&mesh, &statusfn] (const XYRotation &rot) [&mesh, &statusfn] (const XYRotation &rot)
@ -350,8 +369,7 @@ Vec2d find_least_supports_rotation(const ModelObject & mo,
// We will use only one instance of this converted mesh to examine different // We will use only one instance of this converted mesh to examine different
// rotations // rotations
TriangleMesh mesh = mo.raw_mesh(); TriangleMesh mesh = get_mesh_to_rotate(mo);
mesh.require_shared_vertices();
// To keep track of the number of iterations // To keep track of the number of iterations
unsigned status = 0; unsigned status = 0;

View File

@ -519,7 +519,7 @@ bool SupportTreeBuildsteps::create_ground_pillar(const Vec3d &hjp,
auto [polar, azimuth] = dir_to_spheric(dir); auto [polar, azimuth] = dir_to_spheric(dir);
polar = PI - m_cfg.bridge_slope; polar = PI - m_cfg.bridge_slope;
Vec3d d = spheric_to_dir(polar, azimuth).normalized(); Vec3d d = spheric_to_dir(polar, azimuth).normalized();
double t = bridge_mesh_distance(endp, dir, radius); double t = bridge_mesh_distance(endp, d, radius);
double tmax = std::min(m_cfg.max_bridge_length_mm, t); double tmax = std::min(m_cfg.max_bridge_length_mm, t);
t = 0.; t = 0.;

View File

@ -303,13 +303,16 @@ void GLVolume::SinkingContours::render()
void GLVolume::SinkingContours::update() void GLVolume::SinkingContours::update()
{ {
if (m_parent.is_sinking() && !m_parent.is_below_printbed()) { int object_idx = m_parent.object_idx();
Model& model = GUI::wxGetApp().plater()->model();
if (0 <= object_idx && object_idx < (int)model.objects.size() && m_parent.is_sinking() && !m_parent.is_below_printbed()) {
const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box(); const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box();
if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) { if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) {
m_old_box = box; m_old_box = box;
m_shift = Vec3d::Zero(); m_shift = Vec3d::Zero();
const TriangleMesh& mesh = GUI::wxGetApp().plater()->model().objects[m_parent.object_idx()]->volumes[m_parent.volume_idx()]->mesh(); const TriangleMesh& mesh = model.objects[object_idx]->volumes[m_parent.volume_idx()]->mesh();
assert(mesh.has_shared_vertices()); assert(mesh.has_shared_vertices());
m_model.reset(); m_model.reset();

View File

@ -6373,7 +6373,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
case EWarning::ObjectOutside: text = _u8L("An object outside the print area was detected."); break; case EWarning::ObjectOutside: text = _u8L("An object outside the print area was detected."); break;
case EWarning::ToolpathOutside: text = _u8L("A toolpath outside the print area was detected."); error = ErrorType::SLICING_ERROR; break; case EWarning::ToolpathOutside: text = _u8L("A toolpath outside the print area was detected."); error = ErrorType::SLICING_ERROR; break;
case EWarning::SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = ErrorType::PLATER_ERROR; break; case EWarning::SlaSupportsOutside: text = _u8L("SLA supports outside the print area were detected."); error = ErrorType::PLATER_ERROR; break;
case EWarning::SomethingNotShown: text = _u8L("Some objects are not visible."); break; case EWarning::SomethingNotShown: text = _u8L("Some objects are not visible during editing."); break;
case EWarning::ObjectClashed: case EWarning::ObjectClashed:
text = _u8L("An object outside the print area was detected.\n" text = _u8L("An object outside the print area was detected.\n"
"Resolve the current problem to continue slicing."); "Resolve the current problem to continue slicing.");

View File

@ -1778,10 +1778,8 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
del_layers_from_object(obj_idx); del_layers_from_object(obj_idx);
else if (type & itLayer && obj_idx != -1) else if (type & itLayer && obj_idx != -1)
del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item)); del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item));
else if (type & itInfo && obj_idx != -1) { else if (type & itInfo && obj_idx != -1)
Unselect(item); del_info_item(obj_idx, m_objects_model->GetInfoItemType(item));
Select(parent);
}
else if (idx == -1) else if (idx == -1)
return; return;
else if (!del_subobject_from_object(obj_idx, idx, type)) else if (!del_subobject_from_object(obj_idx, idx, type))
@ -1795,6 +1793,52 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
update_info_items(obj_idx); update_info_items(obj_idx);
} }
void ObjectList::del_info_item(const int obj_idx, InfoItemType type)
{
Plater* plater = wxGetApp().plater();
GLCanvas3D* cnv = plater->canvas3D();
switch (type) {
case InfoItemType::CustomSupports:
cnv->get_gizmos_manager().reset_all_states();
Plater::TakeSnapshot(plater, _L("Remove paint-on supports"));
for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes)
mv->supported_facets.clear();
break;
case InfoItemType::CustomSeam:
cnv->get_gizmos_manager().reset_all_states();
Plater::TakeSnapshot(plater, _L("Remove paint-on seam"));
for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes)
mv->seam_facets.clear();
break;
case InfoItemType::MmuSegmentation:
cnv->get_gizmos_manager().reset_all_states();
Plater::TakeSnapshot(plater, _L("Remove Multi Material painting"));
for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes)
mv->mmu_segmentation_facets.clear();
break;
case InfoItemType::Sinking:
Plater::TakeSnapshot(plater, _L("Shift objects to bed"));
(*m_objects)[obj_idx]->ensure_on_bed();
cnv->reload_scene(true, true);
break;
case InfoItemType::VariableLayerHeight:
Plater::TakeSnapshot(plater, _L("Remove variable layer height"));
(*m_objects)[obj_idx]->layer_height_profile.clear();
if (cnv->is_layers_editing_enabled())
//cnv->post_event(SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING));
cnv->force_main_toolbar_left_action(cnv->get_main_toolbar_item_id("layersediting"));
break;
case InfoItemType::Undef : assert(false); break;
}
cnv->post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
{ {
const bool is_layer_settings = m_objects_model->GetItemType(parent_item) == itLayer; const bool is_layer_settings = m_objects_model->GetItemType(parent_item) == itLayer;

View File

@ -254,6 +254,7 @@ public:
void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range); void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range);
void del_layers_from_object(const int obj_idx); void del_layers_from_object(const int obj_idx);
bool del_subobject_from_object(const int obj_idx, const int idx, const int type); bool del_subobject_from_object(const int obj_idx, const int idx, const int type);
void del_info_item(const int obj_idx, InfoItemType type);
void split(); void split();
void merge(bool to_multipart_object); void merge(bool to_multipart_object);
void layers_editing(); void layers_editing();

View File

@ -789,8 +789,8 @@ void Preview::update_layers_slider_mode()
return false; return false;
for (ModelVolume* volume : object->volumes) for (ModelVolume* volume : object->volumes)
if (volume->config.has("extruder") && if ((volume->config.has("extruder") &&
volume->config.option("extruder")->getInt() != extruder || volume->config.option("extruder")->getInt() != extruder) ||
!volume->mmu_segmentation_facets.empty()) !volume->mmu_segmentation_facets.empty())
return false; return false;

View File

@ -55,8 +55,15 @@ std::string GLGizmoCut::on_get_name() const
void GLGizmoCut::on_set_state() void GLGizmoCut::on_set_state()
{ {
// Reset m_cut_z on gizmo activation // Reset m_cut_z on gizmo activation
#if ENABLE_SINKING_CONTOURS
if (get_state() == On) {
m_cut_z = bounding_box().center().z();
m_cut_contours.reset();
}
#else
if (get_state() == On) if (get_state() == On)
m_cut_z = bounding_box().center().z(); m_cut_z = bounding_box().center().z();
#endif // ENABLE_SINKING_CONTOURS
} }
bool GLGizmoCut::on_is_activable() const bool GLGizmoCut::on_is_activable() const
@ -209,8 +216,12 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit)
m_imgui->end(); m_imgui->end();
if (cut_clicked && (m_keep_upper || m_keep_lower)) if (cut_clicked && (m_keep_upper || m_keep_lower)) {
perform_cut(m_parent.get_selection()); perform_cut(m_parent.get_selection());
#if ENABLE_SINKING_CONTOURS
m_cut_contours.reset();
#endif // ENABLE_SINKING_CONTOURS
}
} }
void GLGizmoCut::set_cut_z(double cut_z) void GLGizmoCut::set_cut_z(double cut_z)
@ -308,9 +319,8 @@ void GLGizmoCut::update_contours()
m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f }); m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f });
} }
} }
else if (box.center() != m_cut_contours.position) { else if (box.center() != m_cut_contours.position)
m_cut_contours.shift = box.center() - m_cut_contours.position; m_cut_contours.shift = box.center() - m_cut_contours.position;
}
} }
else else
m_cut_contours.contours.reset(); m_cut_contours.contours.reset();

View File

@ -35,6 +35,16 @@ class GLGizmoCut : public GLGizmoBase
Vec3d shift{ Vec3d::Zero() }; Vec3d shift{ Vec3d::Zero() };
int object_idx{ -1 }; int object_idx{ -1 };
int instance_idx{ -1 }; int instance_idx{ -1 };
void reset() {
mesh.clear();
contours.reset();
cut_z = 0.0;
position = Vec3d::Zero();
shift = Vec3d::Zero();
object_idx = -1;
instance_idx = -1;
}
}; };
CutContours m_cut_contours; CutContours m_cut_contours;

View File

@ -497,9 +497,6 @@ void GLGizmoRotate3D::on_render()
m_gizmos[Z].render(); m_gizmos[Z].render();
} }
const char * GLGizmoRotate3D::RotoptimzeWindow::options[RotoptimizeJob::get_methods_count()];
bool GLGizmoRotate3D::RotoptimzeWindow::options_valid = false;
GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui, GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui,
State & state, State & state,
const Alignment &alignment) const Alignment &alignment)
@ -515,21 +512,26 @@ GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui,
y = std::min(y, alignment.bottom_limit - win_h); y = std::min(y, alignment.bottom_limit - win_h);
ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always); ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always);
ImGui::PushItemWidth(200.f); ImGui::PushItemWidth(300.f);
size_t methods_cnt = RotoptimizeJob::get_methods_count(); if (ImGui::BeginCombo(_L("Choose goal").c_str(), RotoptimizeJob::get_method_name(state.method_id).c_str())) {
if (!options_valid) { for (size_t i = 0; i < RotoptimizeJob::get_methods_count(); ++i) {
for (size_t i = 0; i < methods_cnt; ++i) if (ImGui::Selectable(RotoptimizeJob::get_method_name(i).c_str())) {
options[i] = RotoptimizeJob::get_method_names()[i].c_str(); state.method_id = i;
wxGetApp().app_config->set("sla_auto_rotate",
"method_id",
std::to_string(state.method_id));
}
options_valid = true; if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(i).c_str());
}
ImGui::EndCombo();
} }
int citem = state.method_id; if (ImGui::IsItemHovered())
if (ImGui::Combo(_L("Choose goal").c_str(), &citem, options, methods_cnt) ) { ImGui::SetTooltip("%s", RotoptimizeJob::get_method_description(state.method_id).c_str());
state.method_id = citem;
wxGetApp().app_config->set("sla_auto_rotate", "method_id", std::to_string(state.method_id));
}
ImGui::Separator(); ImGui::Separator();

View File

@ -138,10 +138,6 @@ private:
class RotoptimzeWindow { class RotoptimzeWindow {
ImGuiWrapper *m_imgui = nullptr; ImGuiWrapper *m_imgui = nullptr;
static const char * options [];
static bool options_valid;
public: public:
struct State { struct State {

View File

@ -447,7 +447,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
} }
if (action == SLAGizmoEventType::DiscardChanges) { if (action == SLAGizmoEventType::DiscardChanges) {
editing_mode_discard_changes(); ask_about_changes_call_after([this](){ editing_mode_apply_changes(); },
[this](){ editing_mode_discard_changes(); });
return true; return true;
} }
@ -879,6 +880,22 @@ CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const
void GLGizmoSlaSupports::ask_about_changes_call_after(std::function<void()> on_yes, std::function<void()> on_no)
{
wxGetApp().CallAfter([this, on_yes, on_no]() {
// Following is called through CallAfter, because otherwise there was a problem
// on OSX with the wxMessageDialog being shown several times when clicked into.
MessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually "
"edited support points?") + "\n",_L("Save support points?"), wxICON_QUESTION | wxYES | wxNO | wxCANCEL );
int ret = dlg.ShowModal();
if (ret == wxID_YES)
on_yes();
else if (ret == wxID_NO)
on_no();
});
}
void GLGizmoSlaSupports::on_set_state() void GLGizmoSlaSupports::on_set_state()
{ {
if (m_state == m_old_state) if (m_state == m_old_state)
@ -901,18 +918,8 @@ void GLGizmoSlaSupports::on_set_state()
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
bool will_ask = m_editing_mode && unsaved_changes() && on_is_activable(); bool will_ask = m_editing_mode && unsaved_changes() && on_is_activable();
if (will_ask) { if (will_ask) {
wxGetApp().CallAfter([this]() { ask_about_changes_call_after([this](){ editing_mode_apply_changes(); },
// Following is called through CallAfter, because otherwise there was a problem [this](){ editing_mode_discard_changes(); });
// on OSX with the wxMessageDialog being shown several times when clicked into.
//wxMessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually "
MessageDialog dlg(GUI::wxGetApp().mainframe, _L("Do you want to save your manually "
"edited support points?") + "\n",_L("Save support points?"), wxICON_QUESTION | wxYES | wxNO | wxCANCEL );
int ret = dlg.ShowModal();
if (ret == wxID_YES)
editing_mode_apply_changes();
else if (ret == wxID_NO)
editing_mode_discard_changes();
});
// refuse to be turned off so the gizmo is active when the CallAfter is executed // refuse to be turned off so the gizmo is active when the CallAfter is executed
m_state = m_old_state; m_state = m_old_state;
} }

View File

@ -117,6 +117,7 @@ private:
void auto_generate(); void auto_generate();
void switch_to_editing_mode(); void switch_to_editing_mode();
void disable_editing_mode(); void disable_editing_mode();
void ask_about_changes_call_after(std::function<void()> on_yes, std::function<void()> on_no);
protected: protected:
void on_set_state() override; void on_set_state() override;

View File

@ -15,14 +15,21 @@ class RotoptimizeJob : public PlaterJob
using FindFn = std::function<Vec2d(const ModelObject & mo, using FindFn = std::function<Vec2d(const ModelObject & mo,
const sla::RotOptimizeParams &params)>; const sla::RotOptimizeParams &params)>;
struct FindMethod { std::string name; FindFn findfn; }; struct FindMethod { std::string name; FindFn findfn; std::string descr; };
static inline const FindMethod Methods[] = { static inline const FindMethod Methods[]
{ L("Best surface quality"), sla::find_best_misalignment_rotation }, = {{L("Best surface quality"),
{ L("Least supports"), sla::find_least_supports_rotation }, sla::find_best_misalignment_rotation,
// Just a min area bounding box that is done for all methods anyway. L("Optimize object rotation for best surface quality.")},
{ L("Z axis only"), nullptr } {L("Reduced overhang slopes"),
}; sla::find_least_supports_rotation,
L("Optimize object rotation to have minimum amount of overhangs needing support "
"structures.\nNote that this method will try to find the best surface of the object "
"for touching the print bed if no elevation is set.")},
// Just a min area bounding box that is done for all methods anyway.
{L("Smallest bounding box (Z axis only)"),
nullptr,
L("Rotate the object only in Z axis to have the smallest bounding box.")}};
size_t m_method_id = 0; size_t m_method_id = 0;
float m_accuracy = 0.75; float m_accuracy = 0.75;
@ -52,20 +59,15 @@ public:
void finalize() override; void finalize() override;
static constexpr size_t get_methods_count() { return std::size(Methods); } static constexpr size_t get_methods_count() { return std::size(Methods); }
static const auto & get_method_names()
static std::string get_method_name(size_t i)
{ {
static bool m_method_names_valid = false; return _utf8(Methods[i].name);
static std::array<std::string, std::size(Methods)> m_method_names; }
if (!m_method_names_valid) { static std::string get_method_description(size_t i)
{
for (size_t i = 0; i < std::size(Methods); ++i) return _utf8(Methods[i].descr);
m_method_names[i] = _utf8(Methods[i].name);
m_method_names_valid = true;
}
return m_method_names;
} }
}; };

View File

@ -1141,7 +1141,7 @@ void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type
if (!node || if (!node ||
node->GetIdx() <-1 || node->GetIdx() <-1 ||
( node->GetIdx() == -1 && ( node->GetIdx() == -1 &&
!(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot/* | itLayer*/)) !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot | itInfo))
) )
) )
return; return;