Remove code duplication, clarify naming of orientation searches

This commit is contained in:
tamasmeszaros 2021-08-18 16:47:59 +02:00
parent 74edeb147b
commit 1672130d45
2 changed files with 84 additions and 135 deletions

View File

@ -283,64 +283,61 @@ 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); template<unsigned MAX_ITER>
struct RotfinderBoilerplate {
static constexpr unsigned MAX_TRIES = MAX_ITER;
return mesh; int status = 0;
} TriangleMesh mesh;
unsigned max_tries;
const RotOptimizeParams &params;
// Assemble the mesh with the correct transformation to be used in rotation
// optimization.
static 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;
}
RotfinderBoilerplate(const ModelObject &mo, const RotOptimizeParams &p)
: mesh{get_mesh_to_rotate(mo)}
, params{p}
, max_tries(p.accuracy() * MAX_TRIES)
{
}
void statusfn() { params.statuscb()(++status * 100.0 / max_tries); }
bool stopcond() { return ! params.statuscb()(-1); }
};
Vec2d find_best_misalignment_rotation(const ModelObject & mo, Vec2d find_best_misalignment_rotation(const ModelObject & mo,
const RotOptimizeParams &params) const RotOptimizeParams &params)
{ {
static constexpr unsigned MAX_TRIES = 1000; RotfinderBoilerplate<1000> bp{mo, params};
// return value
XYRotation rot;
// We will use only one instance of this converted mesh to examine different
// rotations
TriangleMesh mesh = get_mesh_to_rotate(mo);
// To keep track of the number of iterations
int status = 0;
// The maximum number of iterations
auto max_tries = unsigned(params.accuracy() * MAX_TRIES);
auto &statuscb = params.statuscb();
// call status callback with zero, because we are at the start
statuscb(status);
auto statusfn = [&statuscb, &status, &max_tries] {
// report status
statuscb(++status * 100.0/max_tries);
};
auto stopcond = [&statuscb] {
return ! statuscb(-1);
};
// Preparing the optimizer. // Preparing the optimizer.
size_t gridsize = std::sqrt(max_tries); size_t gridsize = std::sqrt(bp.max_tries);
opt::Optimizer<opt::AlgBruteForce> solver(opt::StopCriteria{} opt::Optimizer<opt::AlgBruteForce> solver(
.max_iterations(max_tries) opt::StopCriteria{}.max_iterations(bp.max_tries)
.stop_condition(stopcond), .stop_condition([&bp] { return bp.stopcond(); }),
gridsize); gridsize
);
// 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.
@ -348,48 +345,19 @@ Vec2d find_best_misalignment_rotation(const ModelObject & mo,
auto bounds = opt::bounds({ {-PI, PI}, {-PI, PI} }); 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) [&bp] (const XYRotation &rot)
{ {
statusfn(); bp.statusfn();
return get_misalginment_score(mesh, to_transform3f(rot)); return get_misalginment_score(bp.mesh, to_transform3f(rot));
}, opt::initvals({0., 0.}), bounds); }, opt::initvals({0., 0.}), bounds);
rot = result.optimum; return {result.optimum[0], result.optimum[1]};
return {rot[0], rot[1]};
} }
Vec2d find_least_supports_rotation(const ModelObject & mo, Vec2d find_least_supports_rotation(const ModelObject & mo,
const RotOptimizeParams &params) const RotOptimizeParams &params)
{ {
static const unsigned MAX_TRIES = 1000; RotfinderBoilerplate<1000> bp{mo, params};
// return value
XYRotation rot;
// We will use only one instance of this converted mesh to examine different
// rotations
TriangleMesh mesh = get_mesh_to_rotate(mo);
// To keep track of the number of iterations
unsigned status = 0;
// The maximum number of iterations
auto max_tries = unsigned(params.accuracy() * MAX_TRIES);
auto &statuscb = params.statuscb();
// call status callback with zero, because we are at the start
statuscb(status);
auto statusfn = [&statuscb, &status, &max_tries] {
// report status
statuscb(unsigned(++status * 100.0/max_tries) );
};
auto stopcond = [&statuscb] {
return ! statuscb(-1);
};
SLAPrintObjectConfig pocfg; SLAPrintObjectConfig pocfg;
if (params.print_config()) if (params.print_config())
@ -397,31 +365,35 @@ Vec2d find_least_supports_rotation(const ModelObject & mo,
pocfg.apply(mo.config.get()); pocfg.apply(mo.config.get());
XYRotation rot;
// Different search methods have to be used depending on the model elevation // Different search methods have to be used depending on the model elevation
if (is_on_floor(pocfg)) { if (is_on_floor(pocfg)) {
std::vector<XYRotation> inputs = get_chull_rotations(mesh, max_tries); std::vector<XYRotation> inputs = get_chull_rotations(bp.mesh, bp.max_tries);
max_tries = inputs.size(); bp.max_tries = inputs.size();
// If the model can be placed on the bed directly, we only need to // If the model can be placed on the bed directly, we only need to
// check the 3D convex hull face rotations. // check the 3D convex hull face rotations.
auto objfn = [&mesh, &statusfn](const XYRotation &rot) { auto objfn = [&bp](const XYRotation &rot) {
statusfn(); bp.statusfn();
Transform3f tr = to_transform3f(rot); Transform3f tr = to_transform3f(rot);
return get_supportedness_onfloor_score(mesh, tr); return get_supportedness_onfloor_score(bp.mesh, tr);
}; };
rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), stopcond); rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), [&bp] {
return bp.stopcond();
});
} else { } else {
// Preparing the optimizer. // Preparing the optimizer.
size_t gridsize = std::sqrt(max_tries); // 2D grid has gridsize^2 calls size_t gridsize = std::sqrt(bp.max_tries); // 2D grid has gridsize^2 calls
opt::Optimizer<opt::AlgBruteForce> solver(opt::StopCriteria{} opt::Optimizer<opt::AlgBruteForce> solver(
.max_iterations(max_tries) opt::StopCriteria{}.max_iterations(bp.max_tries)
.stop_condition(stopcond), .stop_condition([&bp] { return bp.stopcond(); }),
gridsize); gridsize
);
// 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.
@ -429,10 +401,10 @@ Vec2d find_least_supports_rotation(const ModelObject & mo,
auto bounds = opt::bounds({ {-PI, PI}, {-PI, PI} }); auto bounds = opt::bounds({ {-PI, PI}, {-PI, PI} });
auto result = solver.to_min().optimize( auto result = solver.to_min().optimize(
[&mesh, &statusfn] (const XYRotation &rot) [&bp] (const XYRotation &rot)
{ {
statusfn(); bp.statusfn();
return get_supportedness_score(mesh, to_transform3f(rot)); return get_supportedness_score(bp.mesh, to_transform3f(rot));
}, opt::initvals({0., 0.}), bounds); }, opt::initvals({0., 0.}), bounds);
// Save the result // Save the result
@ -442,7 +414,8 @@ Vec2d find_least_supports_rotation(const ModelObject & mo,
return {rot[0], rot[1]}; return {rot[0], rot[1]};
} }
inline BoundingBoxf3 bounding_box_with_tr(const indexed_triangle_set& its, const Transform3f &tr) inline BoundingBoxf3 bounding_box_with_tr(const indexed_triangle_set &its,
const Transform3f &tr)
{ {
if (its.vertices.empty()) if (its.vertices.empty())
return {}; return {};
@ -458,38 +431,12 @@ inline BoundingBoxf3 bounding_box_with_tr(const indexed_triangle_set& its, const
return {bmin.cast<double>(), bmax.cast<double>()}; return {bmin.cast<double>(), bmax.cast<double>()};
} }
Vec2d find_min_z_height_rotation(const ModelObject &mo, const RotOptimizeParams &params) Vec2d find_min_z_height_rotation(const ModelObject &mo,
const RotOptimizeParams &params)
{ {
static const unsigned MAX_TRIES = 1000; RotfinderBoilerplate<1000> bp{mo, params};
// return value TriangleMesh chull = bp.mesh.convex_hull_3d();
XYRotation rot;
// We will use only one instance of this converted mesh to examine different
// rotations
TriangleMesh mesh = get_mesh_to_rotate(mo);
// To keep track of the number of iterations
unsigned status = 0;
// The maximum number of iterations
auto max_tries = unsigned(params.accuracy() * MAX_TRIES);
auto &statuscb = params.statuscb();
// call status callback with zero, because we are at the start
statuscb(status);
auto statusfn = [&statuscb, &status, &max_tries] {
// report status
statuscb(unsigned(++status * 100.0/max_tries) );
};
auto stopcond = [&statuscb] {
return ! statuscb(-1);
};
TriangleMesh chull = mesh.convex_hull_3d();
chull.require_shared_vertices(); chull.require_shared_vertices();
auto inputs = reserve_vector<XYRotation>(chull.its.indices.size()); auto inputs = reserve_vector<XYRotation>(chull.its.indices.size());
auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) { auto rotcmp = [](const XYRotation &r1, const XYRotation &r2) {
@ -514,18 +461,20 @@ Vec2d find_min_z_height_rotation(const ModelObject &mo, const RotOptimizeParams
} }
inputs.shrink_to_fit(); inputs.shrink_to_fit();
max_tries = inputs.size(); bp.max_tries = inputs.size();
// If the model can be placed on the bed directly, we only need to // If the model can be placed on the bed directly, we only need to
// check the 3D convex hull face rotations. // check the 3D convex hull face rotations.
auto objfn = [&chull, &statusfn](const XYRotation &rot) { auto objfn = [&bp, &chull](const XYRotation &rot) {
statusfn(); bp.statusfn();
Transform3f tr = to_transform3f(rot); Transform3f tr = to_transform3f(rot);
return bounding_box_with_tr(chull.its, tr).size().z(); return bounding_box_with_tr(chull.its, tr).size().z();
}; };
rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), stopcond); XYRotation rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), [&bp] {
return bp.stopcond();
});
return {rot[0], rot[1]}; return {rot[0], rot[1]};
} }

View File

@ -27,9 +27,9 @@ class RotoptimizeJob : public PlaterJob
"structures.\nNote that this method will try to find the best surface of the object " "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.")}, "for touching the print bed if no elevation is set.")},
// Just a min area bounding box that is done for all methods anyway. // Just a min area bounding box that is done for all methods anyway.
{L("Smallest Z height"), {L("Lowest Z height"),
sla::find_min_z_height_rotation, sla::find_min_z_height_rotation,
L("Rotate the model to have least z height for faster print time.")}}; L("Rotate the model to have the lowest z height for faster print time.")}};
size_t m_method_id = 0; size_t m_method_id = 0;
float m_accuracy = 0.75; float m_accuracy = 0.75;