mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-25 23:54:00 -06:00
Merge branch 'tm_rotfinder_fixes'
This commit is contained in:
commit
db7f424e46
4 changed files with 91 additions and 69 deletions
|
@ -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,18 +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 is raised by 1.0 to not be less than zero when squared in the next
|
||||||
phi = phi * (phi >= 0.5f);
|
// step. If phi is greater than 0.5 (slope is > 90 deg) make phi zero
|
||||||
|
// to not skip this face in the overall score.
|
||||||
|
phi = (1.f + 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;
|
||||||
|
|
||||||
// Multiply with the area of the current face
|
// Multiply with the area of the current face
|
||||||
return fc.area * POINTS_PER_UNIT_AREA * phi;
|
return fc.area * POINTS_PER_UNIT_AREA * phi;
|
||||||
|
@ -121,7 +125,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 get_supportedness_score(fc);
|
return scaled<int_fast64_t>(get_supportedness_score(fc));
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t facecount = mesh.its.indices.size();
|
size_t facecount = mesh.its.indices.size();
|
||||||
|
@ -164,7 +168,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 +287,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 ¶ms)
|
const RotOptimizeParams ¶ms)
|
||||||
{
|
{
|
||||||
|
@ -293,8 +317,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;
|
||||||
|
@ -350,8 +373,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;
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -517,19 +514,24 @@ GLGizmoRotate3D::RotoptimzeWindow::RotoptimzeWindow(ImGuiWrapper * imgui,
|
||||||
|
|
||||||
ImGui::PushItemWidth(200.f);
|
ImGui::PushItemWidth(200.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();
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 ¶ms)>;
|
const sla::RotOptimizeParams ¶ms)>;
|
||||||
|
|
||||||
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("Least supports"),
|
||||||
};
|
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("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;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue