mirror of
https://github.com/Dark98/SliceBeam.git
synced 2026-07-02 16:49:02 +00:00
WIP: Port BBL auto-orient
This commit is contained in:
@@ -1237,6 +1237,8 @@ add_library(slic3r
|
||||
|
||||
src/main/jni/libnest2d/src/libnest2d.cpp
|
||||
|
||||
src/main/jni/bbl/Orient.cpp
|
||||
|
||||
src/main/jni/slicebeam/beam_native.cpp
|
||||
src/main/jni/slicebeam/GLModel.cpp
|
||||
src/main/jni/slicebeam/GLShader.cpp
|
||||
|
||||
@@ -456,8 +456,7 @@ public class OrientationMenu extends ListBedMenu {
|
||||
dz %= 360;
|
||||
|
||||
model.rotate(j, Math.toRadians(dx), Math.toRadians(dy), Math.toRadians(dz));
|
||||
model.getBoundingBoxExact(j, bbMin, bbMax);
|
||||
model.translate(j, 0, 0, -bbMin.z);
|
||||
model.ensureOnBed(j);
|
||||
|
||||
fragment.getGlView().getRenderer().setSelectionRotation(0, 0, 0);
|
||||
fragment.getGlView().getRenderer().invalidateGlModel(j);
|
||||
|
||||
@@ -421,8 +421,7 @@ public class GLRenderer implements GLSurfaceView.Renderer {
|
||||
if (minPlane != -1) {
|
||||
GLModel glModel = flattenPlanes.get(minPlane);
|
||||
model.flattenRotate(selectedObject, glModel);
|
||||
model.getBoundingBoxExact(j, bbMin, bbMax);
|
||||
model.translate(j, 0, 0, -bbMin.z);
|
||||
model.ensureOnBed(selectedObject);
|
||||
|
||||
invalidateGlModel(selectedObject);
|
||||
for (int k = 0, l = flattenPlanes.size(); k < l; k++) {
|
||||
|
||||
@@ -86,6 +86,10 @@ public class Model {
|
||||
resetBoundingBox();
|
||||
}
|
||||
|
||||
public void ensureOnBed(int i) {
|
||||
Native.model_ensure_on_bed(pointer, i);
|
||||
}
|
||||
|
||||
public void scale(int i, double x, double y, double z) {
|
||||
Native.model_scale(pointer, i, x, y, z);
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ class Native {
|
||||
static native boolean model_is_left_handed(long ptr, int i);
|
||||
static native void model_translate(long ptr, int i, double x, double y, double z);
|
||||
static native void model_translate_global(long ptr, double x, double y, double z);
|
||||
static native void model_ensure_on_bed(long ptr, int i);
|
||||
static native void model_scale(long ptr, int i, double x, double y, double z);
|
||||
static native void model_rotate(long ptr, int i, double x, double y, double z);
|
||||
static native void model_flatten_rotate(long ptr, int i, long surfacePtr);
|
||||
|
||||
@@ -0,0 +1,610 @@
|
||||
#include "Orient.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include <numeric>
|
||||
#include <ClipperUtils.hpp>
|
||||
#include <boost/geometry/index/rtree.hpp>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include "bbl_utils.hpp"
|
||||
|
||||
#if defined(_MSC_VER) && defined(__clang__)
|
||||
#define BOOST_NO_CXX17_HDR_STRING_VIEW
|
||||
#endif
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/multiprecision/integer.hpp>
|
||||
#include <boost/rational.hpp>
|
||||
|
||||
#undef MAX3
|
||||
#define MAX3(a, b, c) std::max(std::max(a,b),c)
|
||||
|
||||
#undef MEDIAN
|
||||
#define MEDIAN3(a, b, c) std::max(std::min(a,b), std::min(std::max(a,b),c))
|
||||
#ifndef SQ
|
||||
#define SQ(x) ((x)*(x))
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace orientation {
|
||||
|
||||
struct CostItems {
|
||||
float overhang;
|
||||
float bottom;
|
||||
float bottom_hull;
|
||||
float contour;
|
||||
float area_laf; // area_of_low_angle_faces
|
||||
float area_projected; // area of projected 2D profile
|
||||
float volume;
|
||||
float area_total; // total area of all faces
|
||||
float radius; // radius of bounding box
|
||||
float height_to_bottom_hull_ratio; // affects stability, the lower the better
|
||||
float unprintability;
|
||||
|
||||
CostItems(CostItems const &other) = default;
|
||||
|
||||
CostItems() { memset(this, 0, sizeof(*this)); }
|
||||
|
||||
static std::string field_names() {
|
||||
return " overhang, bottom, bothull, contour, A_laf, A_prj, unprintability";
|
||||
}
|
||||
|
||||
std::string field_values() {
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << std::setprecision(1);
|
||||
ss << overhang << ",\t" << bottom << ",\t" << bottom_hull << ",\t" << contour
|
||||
<< ",\t" << area_laf << ",\t" << area_projected << ",\t" << unprintability;
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A class encapsulating the libnest2d Nester class and extending it with other
|
||||
// management and spatial index structures for acceleration.
|
||||
class AutoOrienter {
|
||||
public:
|
||||
int face_count_hull;
|
||||
OrientMesh *orient_mesh = NULL;
|
||||
TriangleMesh *mesh;
|
||||
TriangleMesh mesh_convex_hull;
|
||||
Eigen::MatrixXf normals, normals_quantize, normals_hull, normals_hull_quantize;
|
||||
Eigen::VectorXf areas, areas_hull;
|
||||
Eigen::VectorXf is_apperance; // whether a facet is outer apperance
|
||||
Eigen::MatrixXf z_projected;
|
||||
Eigen::VectorXf z_max, z_max_hull; // max of projected z
|
||||
Eigen::VectorXf z_median; // median of projected z
|
||||
Eigen::VectorXf z_mean; // mean of projected z
|
||||
std::vector<Vec3f> face_normals;
|
||||
std::vector<Vec3f> face_normals_hull;
|
||||
OrientParams params;
|
||||
|
||||
|
||||
std::vector<Vec3f> orientations; // Vec3f == stl_normal
|
||||
std::function<void(unsigned)> progressind = {}; // default empty indicator function
|
||||
|
||||
public:
|
||||
AutoOrienter(OrientMesh *orient_mesh_,
|
||||
const OrientParams ¶ms_,
|
||||
std::function<void(unsigned)> progressind_,
|
||||
std::function<bool(void)> stopcond_) {
|
||||
orient_mesh = orient_mesh_;
|
||||
mesh = &orient_mesh->mesh;
|
||||
params = params_;
|
||||
progressind = progressind_;
|
||||
params.ASCENT = cos(PI - orient_mesh->overhang_angle * PI /
|
||||
180); // use per-object overhang angle
|
||||
|
||||
// BOOST_LOG_TRIVIAL(info) << orient_mesh->name << ", angle=" << orient_mesh->overhang_angle << ", params.ASCENT=" << params.ASCENT;
|
||||
// std::cout << orient_mesh->name << ", angle=" << orient_mesh->overhang_angle << ", params.ASCENT=" << params.ASCENT;
|
||||
|
||||
preprocess();
|
||||
}
|
||||
|
||||
AutoOrienter(TriangleMesh *mesh_) {
|
||||
mesh = mesh_;
|
||||
preprocess();
|
||||
}
|
||||
|
||||
struct VecHash {
|
||||
size_t operator()(const Vec3f &n1) const {
|
||||
return std::hash<coord_t>()(int(n1(0) * 100 + 100)) +
|
||||
std::hash<coord_t>()(int(n1(1) * 100 + 100)) * 101 +
|
||||
std::hash<coord_t>()(int(n1(2) * 100 + 100)) * 10221;
|
||||
}
|
||||
};
|
||||
|
||||
Vec3f quantize_vec3f(const Vec3f n1) {
|
||||
return Vec3f(floor(n1(0) * 1000) / 1000, floor(n1(1) * 1000) / 1000,
|
||||
floor(n1(2) * 1000) / 1000);
|
||||
}
|
||||
|
||||
Vec3d process() {
|
||||
orientations = {{0, 0, -1}}; // original orientation
|
||||
|
||||
area_cumulation_accurate(face_normals, normals_quantize, areas, 10);
|
||||
|
||||
area_cumulation_accurate(face_normals_hull, normals_hull_quantize, areas_hull, 14);
|
||||
|
||||
add_supplements();
|
||||
|
||||
if (progressind)
|
||||
progressind(20);
|
||||
|
||||
remove_duplicates();
|
||||
|
||||
if (progressind)
|
||||
progressind(30);
|
||||
|
||||
std::unordered_map<Vec3f, CostItems, VecHash> results;
|
||||
BOOST_LOG_TRIVIAL(info) << CostItems::field_names();
|
||||
std::cout << CostItems::field_names() << std::endl;
|
||||
for (int i = 0; i < orientations.size(); i++) {
|
||||
auto orientation = -orientations[i];
|
||||
|
||||
project_vertices(orientation);
|
||||
|
||||
auto cost_items = get_features(orientation, params.min_volume);
|
||||
|
||||
float unprintability = target_function(cost_items, params.min_volume);
|
||||
|
||||
results[orientation] = cost_items;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(4) << "orientation:"
|
||||
<< orientation.transpose() << ", cost:" << std::fixed
|
||||
<< std::setprecision(4) << cost_items.field_values();
|
||||
std::cout << std::fixed << std::setprecision(4) << "orientation:"
|
||||
<< orientation.transpose() << ", cost:" << std::fixed
|
||||
<< std::setprecision(4) << cost_items.field_values() << std::endl;
|
||||
}
|
||||
if (progressind)
|
||||
progressind(60);
|
||||
|
||||
typedef std::pair<Vec3f, CostItems> PAIR;
|
||||
std::vector<PAIR> results_vector(results.begin(), results.end());
|
||||
sort(results_vector.begin(), results_vector.end(),
|
||||
[](const PAIR &p1, const PAIR &p2) {
|
||||
return p1.second.unprintability < p2.second.unprintability;
|
||||
});
|
||||
|
||||
if (progressind)
|
||||
progressind(80);
|
||||
|
||||
//To avoid flipping, we need to verify if there are orientations with same unprintability.
|
||||
Vec3f n1 = {0, 0, 1};
|
||||
auto best_orientation = results_vector[0].first;
|
||||
|
||||
for (int i = 1; i < results_vector.size() - 1; i++) {
|
||||
if (abs(results_vector[i].second.unprintability -
|
||||
results_vector[0].second.unprintability) < EPSILON &&
|
||||
abs(results_vector[0].first.dot(n1) - 1) > EPSILON) {
|
||||
if (abs(results_vector[i].first.dot(n1) - 1) < EPSILON * EPSILON) {
|
||||
best_orientation = n1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(6) << "best:"
|
||||
<< best_orientation.transpose() << ", costs:"
|
||||
<< results_vector[0].second.field_values();
|
||||
std::cout << std::fixed << std::setprecision(6) << "best:"
|
||||
<< best_orientation.transpose() << ", costs:"
|
||||
<< results_vector[0].second.field_values() << std::endl;
|
||||
|
||||
return best_orientation.cast<double>();
|
||||
}
|
||||
|
||||
void preprocess() {
|
||||
int count_apperance = 0;
|
||||
{
|
||||
int face_count = mesh->facets_count();
|
||||
auto its = mesh->its;
|
||||
face_normals = its_face_normals(its);
|
||||
areas = Eigen::VectorXf::Zero(face_count);
|
||||
is_apperance = Eigen::VectorXf::Zero(face_count);
|
||||
normals = Eigen::MatrixXf::Zero(face_count, 3);
|
||||
normals_quantize = Eigen::MatrixXf::Zero(face_count, 3);
|
||||
for (size_t i = 0; i < face_count; i++) {
|
||||
float area = get_its_facet_area(its, i);
|
||||
normals.row(i) = face_normals[i];
|
||||
normals_quantize.row(i) = quantize_vec3f(face_normals[i]);
|
||||
areas(i) = area;
|
||||
// TODO: Fix this // is_apperance(i) = (its.get_property(i).type == EnumFaceTypes::eExteriorAppearance);
|
||||
count_apperance += (is_apperance(i) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (orient_mesh)
|
||||
BOOST_LOG_TRIVIAL(debug) << orient_mesh->name << ", count_apperance="
|
||||
<< count_apperance;
|
||||
|
||||
// get convex hull statistics
|
||||
{
|
||||
mesh_convex_hull = mesh->convex_hull_3d();
|
||||
//mesh_convex_hull.write_binary("convex_hull_debug.stl");
|
||||
|
||||
int face_count = mesh_convex_hull.facets_count();
|
||||
auto its = mesh_convex_hull.its;
|
||||
face_count_hull = mesh_convex_hull.facets_count();
|
||||
face_normals_hull = its_face_normals(its);
|
||||
areas_hull = Eigen::VectorXf::Zero(face_count);
|
||||
normals_hull = Eigen::MatrixXf::Zero(face_count_hull, 3);
|
||||
normals_hull_quantize = Eigen::MatrixXf::Zero(face_count_hull, 3);
|
||||
for (size_t i = 0; i < face_count; i++) {
|
||||
float area = get_its_facet_area(its, i);
|
||||
//We cannot use quantized vector here, the accumulated error will result in bad orientations.
|
||||
normals_hull.row(i) = face_normals_hull[i];
|
||||
normals_hull_quantize.row(i) = quantize_vec3f(face_normals_hull[i]);
|
||||
areas_hull(i) = area;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void area_cumulation(const Eigen::MatrixXf &normals_, const Eigen::VectorXf &areas_,
|
||||
int num_directions = 10) {
|
||||
std::unordered_map<stl_normal, float, VecHash> alignments;
|
||||
// init to 0
|
||||
for (auto i = 0; i < areas_.size(); i++)
|
||||
alignments.insert(std::pair(normals_.row(i), 0));
|
||||
// cumulate areas
|
||||
for (auto i = 0; i < areas_.size(); i++) {
|
||||
alignments[normals_.row(i)] += areas_(i);
|
||||
}
|
||||
|
||||
typedef std::pair<stl_normal, float> PAIR;
|
||||
std::vector<PAIR> align_counts(alignments.begin(), alignments.end());
|
||||
sort(align_counts.begin(), align_counts.end(),
|
||||
[](const PAIR &p1, const PAIR &p2) { return p1.second > p2.second; });
|
||||
|
||||
num_directions = std::min((size_t) num_directions, align_counts.size());
|
||||
for (size_t i = 0; i < num_directions; i++) {
|
||||
orientations.push_back(align_counts[i].first);
|
||||
//orientations.push_back(its_face_normals(mesh->its)[i]);
|
||||
BOOST_LOG_TRIVIAL(debug) << align_counts[i].first.transpose() << ", area: "
|
||||
<< align_counts[i].second;
|
||||
}
|
||||
}
|
||||
|
||||
//This function is to make sure to return the accurate normal rather than quantized normal
|
||||
void area_cumulation_accurate(std::vector<Vec3f> &normals_,
|
||||
const Eigen::MatrixXf &quantize_normals_,
|
||||
const Eigen::VectorXf &areas_, int num_directions = 10) {
|
||||
std::unordered_map<stl_normal, std::pair<std::vector<float>, Vec3f>, VecHash> alignments_;
|
||||
Vec3f n1 = {0, 0, 0};
|
||||
std::vector<float> current_areas = {0, 0};
|
||||
// init to 0
|
||||
for (auto i = 0; i < areas_.size(); i++) {
|
||||
alignments_.insert(
|
||||
std::pair(quantize_normals_.row(i), std::pair(current_areas, n1)));
|
||||
}
|
||||
// cumulate areas
|
||||
for (auto i = 0; i < areas_.size(); i++) {
|
||||
alignments_[quantize_normals_.row(i)].first[1] += areas_(i);
|
||||
if (areas_(i) > alignments_[quantize_normals_.row(i)].first[0]) {
|
||||
alignments_[quantize_normals_.row(i)].second = normals_[i];
|
||||
alignments_[quantize_normals_.row(i)].first[0] = areas_(i);
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::pair<stl_normal, std::pair<std::vector<float>, Vec3f>> PAIR;
|
||||
std::vector<PAIR> align_counts(alignments_.begin(), alignments_.end());
|
||||
sort(align_counts.begin(), align_counts.end(), [](const PAIR &p1, const PAIR &p2) {
|
||||
return p1.second.first[1] > p2.second.first[1];
|
||||
});
|
||||
|
||||
num_directions = std::min((size_t) num_directions, align_counts.size());
|
||||
for (size_t i = 0; i < num_directions; i++) {
|
||||
orientations.push_back(align_counts[i].second.second);
|
||||
BOOST_LOG_TRIVIAL(debug) << align_counts[i].second.second.transpose()
|
||||
<< ", area: " << align_counts[i].second.first[1];
|
||||
}
|
||||
}
|
||||
|
||||
void add_supplements() {
|
||||
std::vector<Vec3f> vecs = {{0, 0, -1},
|
||||
{0.70710678, 0, -0.70710678},
|
||||
{0, 0.70710678, -0.70710678},
|
||||
{-0.70710678, 0, -0.70710678},
|
||||
{0, -0.70710678, -0.70710678},
|
||||
{1, 0, 0},
|
||||
{0.70710678, 0.70710678, 0},
|
||||
{0, 1, 0},
|
||||
{-0.70710678, 0.70710678, 0},
|
||||
{-1, 0, 0},
|
||||
{-0.70710678, -0.70710678, 0},
|
||||
{0, -1, 0},
|
||||
{0.70710678, -0.70710678, 0},
|
||||
{0.70710678, 0, 0.70710678},
|
||||
{0, 0.70710678, 0.70710678},
|
||||
{-0.70710678, 0, 0.70710678},
|
||||
{0, -0.70710678, 0.70710678},
|
||||
{0, 0, 1}};
|
||||
orientations.insert(orientations.end(), vecs.begin(), vecs.end());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// remove duplicate orientations
|
||||
/// </summary>
|
||||
/// <param name="tol">tolerance. default 0.01 =sin(0.57\degree)</param>
|
||||
void remove_duplicates(double tol = 0.0000001) {
|
||||
for (auto it = orientations.begin() + 1; it < orientations.end();) {
|
||||
bool duplicate = false;
|
||||
for (auto it_ok = orientations.begin(); it_ok < it; it_ok++) {
|
||||
if (it_ok->isApprox(*it, tol)) {
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const Vec3f all_zero = {0, 0, 0};
|
||||
if (duplicate || it->isApprox(all_zero, tol))
|
||||
it = orientations.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void project_vertices(Vec3f orientation) {
|
||||
int face_count = mesh->facets_count();
|
||||
auto its = mesh->its;
|
||||
z_projected.resize(face_count, 3);
|
||||
z_max.resize(face_count, 1);
|
||||
z_median.resize(face_count, 1);
|
||||
z_mean.resize(face_count, 1);
|
||||
for (size_t i = 0; i < face_count; i++) {
|
||||
float z0 = get_its_vertex(its, i, 0).dot(orientation);
|
||||
float z1 = get_its_vertex(its, i, 1).dot(orientation);
|
||||
float z2 = get_its_vertex(its, i, 2).dot(orientation);
|
||||
z_projected(i, 0) = z0;
|
||||
z_projected(i, 1) = z1;
|
||||
z_projected(i, 2) = z2;
|
||||
z_max(i) = MAX3(z0, z1, z2);
|
||||
z_median(i) = MEDIAN3(z0, z1, z2);
|
||||
z_mean(i) = (z0 + z1 + z2) / 3;
|
||||
}
|
||||
|
||||
z_max_hull.resize(mesh_convex_hull.facets_count(), 1);
|
||||
its = mesh_convex_hull.its;
|
||||
for (auto i = 0; i < z_max_hull.rows(); i++) {
|
||||
float z0 = get_its_vertex(its, i, 0).dot(orientation);
|
||||
float z1 = get_its_vertex(its, i, 1).dot(orientation);
|
||||
float z2 = get_its_vertex(its, i, 2).dot(orientation);
|
||||
z_max_hull(i) = MAX3(z0, z1, z2);
|
||||
}
|
||||
}
|
||||
|
||||
static Eigen::VectorXi
|
||||
argsort(const Eigen::VectorXf &vec, std::string order = "ascend") {
|
||||
Eigen::VectorXi ind = Eigen::VectorXi::LinSpaced(vec.size(), 0,
|
||||
vec.size() - 1);//[0 1 2 3 ... N-1]
|
||||
std::function<bool(int, int)> rule;
|
||||
if (order == "ascend") {
|
||||
rule = [vec](int i, int j) -> bool {
|
||||
return vec(i) < vec(j);
|
||||
};
|
||||
} else {
|
||||
rule = [vec](int i, int j) -> bool {
|
||||
return vec(i) > vec(j);
|
||||
};
|
||||
}
|
||||
std::sort(ind.data(), ind.data() + ind.size(), rule);
|
||||
return ind;
|
||||
|
||||
//sorted_vec.resize(vec.size());
|
||||
//for (int i = 0; i < vec.size(); i++) {
|
||||
// sorted_vec(i) = vec(ind(i));
|
||||
//}
|
||||
}
|
||||
|
||||
// previously calc_overhang
|
||||
CostItems get_features(Vec3f orientation, bool min_volume = true) {
|
||||
CostItems costs;
|
||||
costs.area_total = area_of_boundingbox(mesh->bounding_box());
|
||||
costs.radius = mesh->bounding_box().radius();
|
||||
// volume
|
||||
costs.volume =
|
||||
mesh->stats().volume > 0 ? mesh->stats().volume : its_volume(mesh->its);
|
||||
|
||||
float total_min_z = z_projected.minCoeff();
|
||||
// filter bottom area
|
||||
auto bottom_condition =
|
||||
z_max.array() < total_min_z + this->params.FIRST_LAY_H - EPSILON;
|
||||
auto bottom_condition_hull =
|
||||
z_max_hull.array() < total_min_z + this->params.FIRST_LAY_H - EPSILON;
|
||||
auto bottom_condition_2nd =
|
||||
z_max.array() < total_min_z + this->params.FIRST_LAY_H / 2.f - EPSILON;
|
||||
//The first layer is sliced on half of the first layer height.
|
||||
//The bottom area is measured by accumulating first layer area with the facets area below first layer height.
|
||||
//By combining these two factors, we can avoid the wrong orientation of large planar faces while not influence the
|
||||
//orientations of complex objects with small bottom areas.
|
||||
costs.bottom = bottom_condition.select(areas, 0).sum() * 0.5 +
|
||||
bottom_condition_2nd.select(areas, 0).sum();
|
||||
|
||||
// filter overhang
|
||||
Eigen::VectorXf normal_projection(normals.rows(),
|
||||
1);// = this->normals.dot(orientation);
|
||||
for (auto i = 0; i < normals.rows(); i++) {
|
||||
normal_projection(i) = normals.row(i).dot(orientation);
|
||||
}
|
||||
auto areas_appearance = areas.cwiseProduct(
|
||||
(is_apperance * params.APPERANCE_FACE_SUPP +
|
||||
Eigen::VectorXf::Ones(is_apperance.rows(), is_apperance.cols())));
|
||||
auto overhang_areas = ((normal_projection.array() < params.ASCENT) *
|
||||
(!bottom_condition_2nd)).select(areas_appearance, 0);
|
||||
Eigen::MatrixXf inner = normal_projection.array() - params.ASCENT;
|
||||
inner = inner.cwiseMin(0).cwiseAbs();
|
||||
if (min_volume) {
|
||||
Eigen::MatrixXf heights = z_mean.array() - total_min_z;
|
||||
costs.overhang = (heights.array() * overhang_areas.array() *
|
||||
inner.array()).sum();
|
||||
} else {
|
||||
costs.overhang = overhang_areas.array().cwiseAbs().sum();
|
||||
}
|
||||
|
||||
{
|
||||
// contour perimeter
|
||||
#if 1
|
||||
// the simple way for contour is even better for faces of small bridges
|
||||
costs.contour = 4 * sqrt(costs.bottom);
|
||||
#else
|
||||
float contour = 0;
|
||||
int face_count = mesh->facets_count();
|
||||
auto its = mesh->its;
|
||||
int contour_amout = 0;
|
||||
for (size_t i = 0; i < face_count; i++)
|
||||
{
|
||||
if (bottom_condition(i)) {
|
||||
Eigen::VectorXi index = argsort(z_projected.row(i));
|
||||
stl_vertex line = its.get_vertex(i, index(0)) - its.get_vertex(i, index(1));
|
||||
contour += line.norm();
|
||||
contour_amout++;
|
||||
}
|
||||
}
|
||||
costs.contour += contour + params.CONTOUR_AMOUNT * contour_amout;
|
||||
#endif
|
||||
}
|
||||
|
||||
// bottom of convex hull
|
||||
costs.bottom_hull = (bottom_condition_hull).select(areas_hull, 0).sum();
|
||||
|
||||
// low angle faces
|
||||
auto normal_projection_abs = normal_projection.cwiseAbs();
|
||||
Eigen::MatrixXf laf_areas = ((normal_projection_abs.array() < params.LAF_MAX) *
|
||||
(normal_projection_abs.array() > params.LAF_MIN) *
|
||||
(z_max.array() >
|
||||
total_min_z + params.FIRST_LAY_H)).select(areas, 0);
|
||||
costs.area_laf = laf_areas.sum();
|
||||
|
||||
// height to bottom_hull_area ratio
|
||||
//float total_max_z = z_projected.maxCoeff();
|
||||
//costs.height_to_bottom_hull_ratio = SQ(total_max_z) / (costs.bottom_hull + 1e-7);
|
||||
|
||||
return costs;
|
||||
}
|
||||
|
||||
float target_function(CostItems &costs, bool min_volume) {
|
||||
float cost = 0;
|
||||
float bottom = costs.bottom;//std::min(costs.bottom, params.BOTTOM_MAX);
|
||||
float bottom_hull = costs.bottom_hull;// std::min(costs.bottom_hull, params.BOTTOM_HULL_MAX);
|
||||
if (min_volume) {
|
||||
float overhang = costs.overhang / 25;
|
||||
cost = params.TAR_A * (overhang + params.TAR_B) + params.RELATIVE_F *
|
||||
(/*costs.volume/100*/
|
||||
overhang *
|
||||
params.TAR_C +
|
||||
params.TAR_D +
|
||||
params.TAR_LAF *
|
||||
costs.area_laf *
|
||||
params.use_low_angle_face) /
|
||||
(params.TAR_D +
|
||||
params.CONTOUR_F *
|
||||
costs.contour +
|
||||
params.BOTTOM_F * bottom +
|
||||
params.BOTTOM_HULL_F *
|
||||
bottom_hull +
|
||||
params.TAR_E * overhang +
|
||||
params.TAR_PROJ_AREA *
|
||||
costs.area_projected);
|
||||
} else {
|
||||
float overhang = costs.overhang;
|
||||
cost = params.RELATIVE_F * (costs.overhang * params.TAR_C + params.TAR_D +
|
||||
params.TAR_LAF * costs.area_laf *
|
||||
params.use_low_angle_face) /
|
||||
(params.TAR_D + params.CONTOUR_F * costs.contour +
|
||||
params.BOTTOM_F * bottom + params.BOTTOM_HULL_F * bottom_hull +
|
||||
params.TAR_PROJ_AREA * costs.area_projected);
|
||||
}
|
||||
cost += (costs.bottom < params.BOTTOM_MIN) *
|
||||
100;// +(costs.height_to_bottom_hull_ratio > params.height_to_bottom_hull_ratio_MIN) * 110;
|
||||
|
||||
costs.unprintability = costs.unprintability = cost;
|
||||
|
||||
return cost;
|
||||
}
|
||||
};
|
||||
|
||||
void _orient(OrientMeshs &meshs_,
|
||||
const OrientParams ¶ms,
|
||||
std::function<void(unsigned, std::string)> progressfn,
|
||||
std::function<bool()> stopfn) {
|
||||
if (!params.parallel) {
|
||||
for (size_t i = 0; i != meshs_.size(); ++i) {
|
||||
auto &mesh_ = meshs_[i];
|
||||
progressfn(i, mesh_.name);
|
||||
//auto progressfn_i = [&](unsigned cnt) {progressfn(cnt, "Orienting " + mesh_.name); };
|
||||
AutoOrienter orienter(&mesh_, params, /*progressfn_i*/{}, stopfn);
|
||||
mesh_.orientation = orienter.process();
|
||||
Geometry::rotation_from_two_vectors(mesh_.orientation, {0, 0, 1}, mesh_.axis,
|
||||
mesh_.angle, &mesh_.rotation_matrix);
|
||||
BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(3) << "v,phi: "
|
||||
<< mesh_.axis.transpose() << ", " << mesh_.angle;
|
||||
//flush_logs();
|
||||
}
|
||||
} else {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, meshs_.size()),
|
||||
[&meshs_, ¶ms, progressfn, stopfn](
|
||||
const tbb::blocked_range<size_t> &range) {
|
||||
for (size_t i = range.begin(); i != range.end(); ++i) {
|
||||
auto &mesh_ = meshs_[i];
|
||||
progressfn(i, mesh_.name);
|
||||
AutoOrienter orienter(&mesh_, params, {}, stopfn);
|
||||
mesh_.orientation = orienter.process();
|
||||
Geometry::rotation_from_two_vectors(mesh_.orientation,
|
||||
{0, 0, 1}, mesh_.axis,
|
||||
mesh_.angle,
|
||||
&mesh_.rotation_matrix);
|
||||
mesh_.euler_angles = Geometry::extract_euler_angles(
|
||||
mesh_.rotation_matrix);
|
||||
BOOST_LOG_TRIVIAL(debug) << "rotation_from_two_vectors: "
|
||||
<< mesh_.orientation.transpose()
|
||||
<< "; axis: "
|
||||
<< mesh_.axis.transpose()
|
||||
<< "; angle: " << mesh_.angle
|
||||
<< "; euler: "
|
||||
<< mesh_.euler_angles.transpose()
|
||||
<< ", rotation_matrix:\n"
|
||||
<< mesh_.rotation_matrix;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void orient(OrientMeshs &arrangables,
|
||||
const OrientMeshs &excludes,
|
||||
const OrientParams ¶ms) {
|
||||
|
||||
auto &cfn = params.stopcondition;
|
||||
auto &pri = params.progressind;
|
||||
|
||||
_orient(arrangables, params, pri, cfn);
|
||||
|
||||
}
|
||||
|
||||
void orient(ModelObject *obj) {
|
||||
auto m = obj->mesh();
|
||||
AutoOrienter orienter(&m);
|
||||
Vec3d orientation = orienter.process();
|
||||
Vec3d axis;
|
||||
double angle;
|
||||
Geometry::rotation_from_two_vectors(orientation, {0, 0, 1}, axis, angle, nullptr);
|
||||
|
||||
obj->rotate(angle, axis);
|
||||
obj->ensure_on_bed();
|
||||
}
|
||||
|
||||
void orient(ModelInstance *instance) {
|
||||
auto m = instance->get_object()->mesh();
|
||||
AutoOrienter orienter(&m);
|
||||
Vec3d orientation = orienter.process();
|
||||
Vec3d axis;
|
||||
double angle;
|
||||
Matrix3d rotation_matrix;
|
||||
Geometry::rotation_from_two_vectors(orientation, {0, 0, 1}, axis, angle, &rotation_matrix);
|
||||
|
||||
rotate_model_instance(instance, rotation_matrix);
|
||||
}
|
||||
|
||||
|
||||
} // namespace arr
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,159 @@
|
||||
#ifndef ORIENT_HPP
|
||||
#define ORIENT_HPP
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace orientation {
|
||||
|
||||
|
||||
/// A logical bed representing an object not being orientd. Either the orient
|
||||
/// has not yet successfully run on this OrientPolygon or it could not fit the
|
||||
/// object due to overly large size or invalid geometry.
|
||||
static const constexpr int UNORIENTD = -1;
|
||||
|
||||
/// Input/Output structure for the orient() function. The mesh field will not
|
||||
/// be modified during orientment. Instead, the translation and rotation fields
|
||||
/// will mark the needed transformation for the polygon to be in the orientd
|
||||
/// position. These can also be set to an initial offset and rotation.
|
||||
///
|
||||
/// The bed_idx field will indicate the logical bed into which the
|
||||
/// polygon belongs: UNORIENTD means no place for the polygon
|
||||
/// (also the initial state before orient), 0..N means the index of the bed.
|
||||
/// Zero is the physical bed, larger than zero means a virtual bed.
|
||||
struct OrientMesh {
|
||||
TriangleMesh mesh; /// The real mesh data
|
||||
double overhang_angle = 30;
|
||||
double angle{ 0 };
|
||||
Vec3d axis{ 0,0,1 };
|
||||
Vec3d orientation{ 0,0,1 };
|
||||
Matrix3d rotation_matrix;
|
||||
Vec3d euler_angles;
|
||||
std::string name;
|
||||
|
||||
/// Optional setter function which can store arbitrary data in its closure
|
||||
std::function<void(const OrientMesh&)> setter = nullptr;
|
||||
|
||||
/// Helper function to call the setter with the orient data arguments
|
||||
void apply() const { if (setter) setter(*this); }
|
||||
|
||||
};
|
||||
|
||||
// params for minimizing support area
|
||||
struct OrientParamsArea {
|
||||
float TAR_A = 0.015f;
|
||||
float TAR_B = 0.177f;
|
||||
float RELATIVE_F = 20;
|
||||
float CONTOUR_F = 0.5f;
|
||||
float BOTTOM_F = 2.5f;
|
||||
float BOTTOM_HULL_F = 0.1f;
|
||||
float TAR_C = 0.1f;
|
||||
float TAR_D = 1;
|
||||
float TAR_E = 0.0115f;
|
||||
float FIRST_LAY_H = 0.2f;//0.0475;
|
||||
float VECTOR_TOL = -0.00083f;
|
||||
float NEGL_FACE_SIZE = 0.01f;
|
||||
float ASCENT = -0.5f;
|
||||
float PLAFOND_ADV = 0.0599f;
|
||||
float CONTOUR_AMOUNT = 0.0182427f;
|
||||
float OV_H = 2.574f;
|
||||
float height_offset = 2.3728f;
|
||||
float height_log = 0.041375f;
|
||||
float height_log_k = 1.9325457f;
|
||||
float LAF_MAX = 0.999f; // cos(1.4\degree) for low angle face 0.9997f
|
||||
float LAF_MIN = 0.97f; // cos(14\degree) 0.9703f
|
||||
float TAR_LAF = 0.001f; //0.01f
|
||||
float TAR_PROJ_AREA = 0.1f;
|
||||
float BOTTOM_MIN = 0.1f; // min bottom area. If lower than it the object may be unstable
|
||||
float BOTTOM_MAX = 2000; // max bottom area. If get to it the object is stable enough (further increase bottom area won't do more help)
|
||||
float height_to_bottom_hull_ratio_MIN = 1;
|
||||
float BOTTOM_HULL_MAX = 2000;// max bottom hull area
|
||||
float APPERANCE_FACE_SUPP=3; // penalty of generating supports on appearance face
|
||||
|
||||
float overhang_angle = 60.f;
|
||||
bool use_low_angle_face = true;
|
||||
bool min_volume = false;
|
||||
Eigen::Vector3f fun_dir;
|
||||
|
||||
/// Allow parallel execution.
|
||||
bool parallel = true;
|
||||
|
||||
/// Progress indicator callback called when an object gets packed.
|
||||
/// The unsigned argument is the number of items remaining to pack.
|
||||
std::function<void(unsigned, std::string)> progressind = {};
|
||||
|
||||
/// A predicate returning true if abort is needed.
|
||||
std::function<bool(void)> stopcondition = {};
|
||||
|
||||
OrientParamsArea() = default;
|
||||
};
|
||||
|
||||
struct OrientParams {
|
||||
float TAR_A = 0.01f;//0.128f;
|
||||
float TAR_B = 0.177f;
|
||||
float RELATIVE_F= 6.610621027964314f;
|
||||
float CONTOUR_F = 0.23228623269775997f;
|
||||
float BOTTOM_F = 1.167152017941474f;
|
||||
float BOTTOM_HULL_F = 0.1f;
|
||||
float TAR_C = 0.24308070476924726f;
|
||||
float TAR_D = 0.6284515508160871f;
|
||||
float TAR_E = 0;//0.032157292647062234;
|
||||
float FIRST_LAY_H = 0.2f;//0.029;
|
||||
float VECTOR_TOL = -0.0011163303070972383f;
|
||||
float NEGL_FACE_SIZE = 0.1f;
|
||||
float ASCENT= -0.5f;
|
||||
float PLAFOND_ADV = 0.04079208948120519f;
|
||||
float CONTOUR_AMOUNT = 0.0101472219892684f;
|
||||
float OV_H = 1.0370178217794535f;
|
||||
float height_offset = 2.7417608343142073f;
|
||||
float height_log = 0.06442030687034085f;
|
||||
float height_log_k = 0.3933594673063997f;
|
||||
float LAF_MAX = 0.999f; // cos(1.4\degree) for low angle face //0.9997f;
|
||||
float LAF_MIN= 0.9703f; // cos(14\degree) 0.9703f;
|
||||
float TAR_LAF = 0.01f; //0.1f
|
||||
float TAR_PROJ_AREA = 0.1f;
|
||||
float BOTTOM_MIN = 0.1f; // min bottom area. If lower than it the objects may be unstable
|
||||
float BOTTOM_MAX = 2000; //400
|
||||
float height_to_bottom_hull_ratio_MIN = 1;
|
||||
float BOTTOM_HULL_MAX = 2000;// max bottom hull area to clip //600
|
||||
float APPERANCE_FACE_SUPP=3; // penalty of generating supports on appearance face
|
||||
|
||||
float overhang_angle = 60.f;
|
||||
bool use_low_angle_face = true;
|
||||
bool min_volume = false;
|
||||
Eigen::Vector3f fun_dir;
|
||||
|
||||
|
||||
/// Allow parallel execution.
|
||||
bool parallel = false;
|
||||
|
||||
/// Progress indicator callback called when an object gets packed.
|
||||
/// The unsigned argument is the number of items remaining to pack.
|
||||
std::function<void(unsigned, std::string)> progressind = {};
|
||||
|
||||
/// A predicate returning true if abort is needed.
|
||||
std::function<bool(void)> stopcondition = {};
|
||||
|
||||
OrientParams() = default;
|
||||
};
|
||||
|
||||
using OrientMeshs = std::vector<OrientMesh>;
|
||||
|
||||
/**
|
||||
* \brief Orients the input polygons.
|
||||
|
||||
* \param items Input vector of OrientMeshs. The transformation, rotation
|
||||
* and bin_idx fields will be changed after the call finished and can be used
|
||||
* to apply the result on the input polygon.
|
||||
*/
|
||||
void orient(OrientMeshs &items, const OrientMeshs &excludes, const OrientParams ¶ms = {});
|
||||
|
||||
// this function should be deleted, since rotating objects are so complicated that its inherited transformation may be a trouble
|
||||
void orient(ModelObject* obj);
|
||||
|
||||
void orient(ModelInstance* instance);
|
||||
|
||||
}} // namespace Slic3r::orientment
|
||||
|
||||
#endif // MODELORIENT_HPP
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef BBL_UTILS_HPP
|
||||
#define BBL_UTILS_HPP
|
||||
|
||||
#include "libslic3r/Point.hpp"
|
||||
|
||||
typedef enum {
|
||||
eNormal, // normal face
|
||||
eSmallOverhang, // small overhang
|
||||
eSmallHole, // face with small hole
|
||||
eExteriorAppearance, // exterior appearance
|
||||
eMaxNumFaceTypes
|
||||
}EnumFaceTypes;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace Geometry {
|
||||
void rotation_from_two_vectors(Vec3d& from, Vec3d to, Vec3d& rotation_axis, double& phi, Matrix3d* rotation_matrix) {
|
||||
const Matrix3d m = Eigen::Quaterniond().setFromTwoVectors(from, to).toRotationMatrix();
|
||||
const Eigen::AngleAxisd aa(m);
|
||||
rotation_axis = aa.axis();
|
||||
phi = aa.angle();
|
||||
if (rotation_matrix)
|
||||
*rotation_matrix = m;
|
||||
}
|
||||
|
||||
Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& rotation_matrix) {
|
||||
// The extracted "rotation" is a triplet of numbers such that Geometry::rotation_transform
|
||||
// returns the original transform. Because of the chosen order of rotations, the triplet
|
||||
// is not equivalent to Euler angles in the usual sense.
|
||||
Vec3d angles = rotation_matrix.eulerAngles(2,1,0);
|
||||
std::swap(angles(0), angles(2));
|
||||
return angles;
|
||||
}
|
||||
}
|
||||
|
||||
double area_of_boundingbox(BoundingBoxf3 bb) {
|
||||
return double(bb.max(0) - bb.min(0)) * (bb.max(1) - bb.min(1));
|
||||
}
|
||||
|
||||
stl_vertex get_its_vertex(indexed_triangle_set& its, int facet_idx, int vertex_idx) {
|
||||
return its.vertices[its.indices[facet_idx][vertex_idx]];
|
||||
}
|
||||
|
||||
float get_its_facet_area(indexed_triangle_set& its, int facet_idx) {
|
||||
return std::abs((get_its_vertex(its, facet_idx, 0) - get_its_vertex(its, facet_idx, 1))
|
||||
.cross(get_its_vertex(its, facet_idx, 0) - get_its_vertex(its, facet_idx, 2)).norm()) / 2;
|
||||
}
|
||||
|
||||
void rotate_model_instance(ModelInstance* obj, Matrix3d& rotation_matrix) {
|
||||
auto m_transformation = obj->get_transformation();
|
||||
auto rotation = m_transformation.get_rotation_matrix();
|
||||
rotation = rotation_matrix * rotation;
|
||||
obj->set_rotation(Geometry::Transformation(rotation).get_rotation());
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BBL_UTILS_HPP
|
||||
@@ -458,6 +458,11 @@ extern "C" {
|
||||
model->model.objects[i]->translate(x, y, z);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_model_1ensure_1on_1bed(JNIEnv* env, jclass, jlong ptr, jint i) {
|
||||
ModelRef* model = (ModelRef *) (intptr_t) ptr;
|
||||
model->model.objects[i]->ensure_on_bed(false);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_ru_ytkab0bp_slicebeam_slic3r_Native_model_1scale(JNIEnv* env, jclass, jlong ptr, jint i, jdouble x, jdouble y, jdouble z) {
|
||||
ModelRef* model = (ModelRef *) (intptr_t) ptr;
|
||||
Vec3d factor(x, y, z);
|
||||
|
||||
Reference in New Issue
Block a user