Beta version of the algorithm
Implemented long unsupported segments detection, which considers also curvature Implemented detection of curved segments at the edge of the previous layer - danger of warping/curling
This commit is contained in:
parent
adf39805bc
commit
706cd63e61
@ -275,6 +275,8 @@ set(SLIC3R_SOURCES
|
||||
TriangleSelector.hpp
|
||||
TriangleSetSampling.cpp
|
||||
TriangleSetSampling.hpp
|
||||
TriangleSelectorWrapper.cpp
|
||||
TriangleSelectorWrapper.hpp
|
||||
MTUtils.hpp
|
||||
Zipper.hpp
|
||||
Zipper.cpp
|
||||
|
@ -402,12 +402,38 @@ void PrintObject::find_supportable_issues()
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Searching supportable issues - start";
|
||||
//TODO status number?
|
||||
m_print->set_status(70, L("Searching supportable issues"));
|
||||
|
||||
if (this->has_support()) {
|
||||
m_print->set_status(75, L("Searching supportable issues"));
|
||||
|
||||
if (!this->m_config.support_material) {
|
||||
std::vector<size_t> problematic_layers = SupportableIssues::quick_search(this);
|
||||
if (!problematic_layers.empty()){
|
||||
//TODO report problems
|
||||
}
|
||||
} else {
|
||||
SupportableIssues::quick_search(this);
|
||||
SupportableIssues::Issues issues = SupportableIssues::full_search(this);
|
||||
if (!issues.supports_nedded.empty()) {
|
||||
auto obj_transform = this->trafo_centered();
|
||||
for (ModelVolume *model_volume : this->model_object()->volumes) {
|
||||
if (model_volume->type() == ModelVolumeType::MODEL_PART) {
|
||||
Transform3d model_transformation = model_volume->get_matrix();
|
||||
Transform3d inv_transform = (obj_transform * model_transformation).inverse();
|
||||
TriangleSelectorWrapper selector { model_volume->mesh() };
|
||||
|
||||
for (const Vec3f &support_point : issues.supports_nedded) {
|
||||
selector.enforce_spot(Vec3f(inv_transform.cast<float>() * support_point), 1.0f);
|
||||
}
|
||||
|
||||
model_volume->supported_facets.set(selector.selector);
|
||||
|
||||
#if 1
|
||||
indexed_triangle_set copy = model_volume->mesh().its;
|
||||
its_transform(copy, obj_transform * model_transformation);
|
||||
its_write_obj(copy,
|
||||
debug_out_path(("model" + std::to_string(model_volume->id().id) + ".obj").c_str()).c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_print->throw_if_canceled();
|
||||
@ -417,6 +443,7 @@ void PrintObject::find_supportable_issues()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrintObject::generate_support_material()
|
||||
{
|
||||
if (this->set_started(posSupportMaterial)) {
|
||||
|
@ -4,18 +4,31 @@
|
||||
#include "tbb/blocked_range.h"
|
||||
#include "tbb/parallel_reduce.h"
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <cmath>
|
||||
|
||||
#include "libslic3r/Layer.hpp"
|
||||
#include "libslic3r/EdgeGrid.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
|
||||
#define DEBUG_FILES
|
||||
|
||||
#ifdef DEBUG_FILES
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
namespace SupportableIssues {
|
||||
|
||||
struct Params {
|
||||
float bridge_distance = 5.0f;
|
||||
float printable_protrusion_distance = 1.0f;
|
||||
};
|
||||
void Issues::add(const Issues &layer_issues) {
|
||||
supports_nedded.insert(supports_nedded.end(),
|
||||
layer_issues.supports_nedded.begin(), layer_issues.supports_nedded.end());
|
||||
curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(),
|
||||
layer_issues.curling_up.end());
|
||||
}
|
||||
|
||||
bool Issues::empty() const {
|
||||
return supports_nedded.empty() && curling_up.empty();
|
||||
}
|
||||
|
||||
namespace Impl {
|
||||
|
||||
@ -34,62 +47,218 @@ struct EdgeGridWrapper {
|
||||
}
|
||||
EdgeGrid::Grid grid;
|
||||
ExPolygons ex_polys;
|
||||
}
|
||||
;
|
||||
};
|
||||
|
||||
EdgeGridWrapper compute_layer_merged_edge_grid(const Layer *layer) {
|
||||
static const float eps = float(scale_(layer->object()->config().slice_closing_radius.value));
|
||||
// merge with offset
|
||||
ExPolygons merged = layer->merged(eps);
|
||||
// ofsset back
|
||||
ExPolygons layer_outline = offset_ex(merged, -eps);
|
||||
#ifdef DEBUG_FILES
|
||||
void debug_export(Issues issues, std::string file_name) {
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
|
||||
float min_region_flow_width { };
|
||||
for (const auto *region : layer->regions()) {
|
||||
min_region_flow_width = std::max(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width());
|
||||
}
|
||||
std::cout << "min_region_flow_width: " << min_region_flow_width << std::endl;
|
||||
return EdgeGridWrapper(scale_(min_region_flow_width), layer_outline);
|
||||
}
|
||||
|
||||
void check_extrusion_entity_stability(const ExtrusionEntity *entity, const EdgeGridWrapper &supported_grid,
|
||||
const Params ¶ms) {
|
||||
if (entity->is_collection()){
|
||||
for (const auto* e: static_cast<ExtrusionEntityCollection>(entity).entities){
|
||||
check_extrusion_entity_stability(e, supported_grid, params);
|
||||
}
|
||||
} else { //single extrusion path, with possible varying parameters
|
||||
entity->as_polyline().points;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void check_layer_stability(const PrintObject *po, size_t layer_idx, const Params ¶ms) {
|
||||
if (layer_idx == 0) {
|
||||
// first layer is usually ok
|
||||
{
|
||||
FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_supports.obj").c_str()).c_str(), "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "Debug files: Couldn't open " << file_name << " for writing";
|
||||
return;
|
||||
}
|
||||
const Layer *layer = po->get_layer(layer_idx);
|
||||
const Layer *prev_layer = layer->lower_layer;
|
||||
EdgeGridWrapper supported_grid = compute_layer_merged_edge_grid(prev_layer);
|
||||
|
||||
for (size_t i = 0; i < issues.supports_nedded.size(); ++i) {
|
||||
fprintf(fp, "v %f %f %f %f %f %f\n",
|
||||
issues.supports_nedded[i](0), issues.supports_nedded[i](1), issues.supports_nedded[i](2),
|
||||
1.0, 0.0, 0.0
|
||||
);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
{
|
||||
FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_curling.obj").c_str()).c_str(), "w");
|
||||
if (fp == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "Debug files: Couldn't open " << file_name << " for writing";
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < issues.curling_up.size(); ++i) {
|
||||
fprintf(fp, "v %f %f %f %f %f %f\n",
|
||||
issues.curling_up[i](0), issues.curling_up[i](1), issues.curling_up[i](2),
|
||||
0.0, 1.0, 0.0
|
||||
);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) {
|
||||
float min_region_flow_width { 1.0f };
|
||||
for (const auto *region : layer->regions()) {
|
||||
min_region_flow_width = std::min(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width());
|
||||
}
|
||||
ExPolygons ex_polygons;
|
||||
for (const LayerRegion *layer_region : layer->regions()) {
|
||||
coordf_t flow_width = coordf_t(
|
||||
scale_(layer_region->flow(FlowRole::frExternalPerimeter).width()));
|
||||
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
|
||||
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
|
||||
if (perimeter->role() == ExtrusionRole::erExternalPerimeter) {
|
||||
check_extrusion_entity_stability(perimeter, supported_grid, params);
|
||||
ex_polygons.push_back(ExPolygon { perimeter->as_polyline().points });
|
||||
} // ex_perimeter
|
||||
} // perimeter
|
||||
} // ex_entity
|
||||
}
|
||||
|
||||
return EdgeGridWrapper(scale_(min_region_flow_width), ex_polygons);
|
||||
}
|
||||
|
||||
Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t layer_idx,
|
||||
float slice_z,
|
||||
coordf_t flow_width,
|
||||
const EdgeGridWrapper &supported_grid,
|
||||
const Params ¶ms) {
|
||||
|
||||
Issues issues { };
|
||||
if (entity->is_collection()) {
|
||||
for (const auto *e : static_cast<const ExtrusionEntityCollection*>(entity)->entities) {
|
||||
issues.add(check_extrusion_entity_stability(e, layer_idx, slice_z, flow_width, supported_grid, params));
|
||||
}
|
||||
} else { //single extrusion path, with possible varying parameters
|
||||
Points points = entity->as_polyline().points;
|
||||
float unsupported_distance = params.bridge_distance + 1.0f;
|
||||
float curvature = 0;
|
||||
float max_curvature = 0;
|
||||
Vec2f tmp = unscale(points[0]).cast<float>();
|
||||
Vec3f prev_point = Vec3f(tmp.x(), tmp.y(), slice_z);
|
||||
|
||||
for (size_t point_index = 0; point_index < points.size(); ++point_index) {
|
||||
std::cout << "index: " << point_index << " dist: " << unsupported_distance << " curvature: "
|
||||
<< curvature << " max curvature: " << max_curvature << std::endl;
|
||||
|
||||
Vec2f tmp = unscale(points[point_index]).cast<float>();
|
||||
Vec3f u_point = Vec3f(tmp.x(), tmp.y(), slice_z);
|
||||
|
||||
coordf_t dist_from_prev_layer { 0 };
|
||||
if (!supported_grid.grid.signed_distance(points[point_index], flow_width, dist_from_prev_layer)) {
|
||||
issues.supports_nedded.push_back(u_point);
|
||||
continue;
|
||||
}
|
||||
|
||||
constexpr float limit_overlap_factor = 0.5;
|
||||
|
||||
if (dist_from_prev_layer > flow_width) { //unsupported
|
||||
std::cout << "index: " << point_index << " unsupported " << std::endl;
|
||||
unsupported_distance += (u_point - prev_point).norm();
|
||||
} else {
|
||||
std::cout << "index: " << point_index << " grounded " << std::endl;
|
||||
unsupported_distance = 0;
|
||||
}
|
||||
|
||||
std::cout << "index: " << point_index << " dfromprev: " << dist_from_prev_layer << std::endl;
|
||||
|
||||
if (dist_from_prev_layer > flow_width * limit_overlap_factor && point_index < points.size() - 1) {
|
||||
const Vec2f v1 = (u_point - prev_point).head<2>();
|
||||
const Vec2f v2 = unscale(points[point_index + 1]).cast<float>() - u_point.head<2>();
|
||||
float dot = v1(0) * v2(0) + v1(1) * v2(1);
|
||||
float cross = v1(0) * v2(1) - v1(1) * v2(0);
|
||||
float angle = float(atan2(float(cross), float(dot)));
|
||||
|
||||
std::cout << "index: " << point_index << " angle: " << angle << std::endl;
|
||||
|
||||
curvature += angle;
|
||||
max_curvature = std::max(abs(curvature), max_curvature);
|
||||
}
|
||||
|
||||
if (!(dist_from_prev_layer > flow_width * limit_overlap_factor)) {
|
||||
std::cout << "index: " << point_index << " reset curvature" << std::endl;
|
||||
max_curvature = 0;
|
||||
curvature = 0;
|
||||
}
|
||||
|
||||
if (unsupported_distance > params.bridge_distance / (1 + int(max_curvature * 9 / PI))) {
|
||||
issues.supports_nedded.push_back(u_point);
|
||||
unsupported_distance = 0;
|
||||
curvature = 0;
|
||||
max_curvature = 0;
|
||||
}
|
||||
|
||||
if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) {
|
||||
issues.curling_up.push_back(u_point);
|
||||
curvature = 0;
|
||||
max_curvature = 0;
|
||||
}
|
||||
|
||||
prev_point = u_point;
|
||||
}
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
//TODO needs revision
|
||||
coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) {
|
||||
switch (role) {
|
||||
case ExtrusionRole::erBridgeInfill:
|
||||
return region->flow(FlowRole::frExternalPerimeter).scaled_width();
|
||||
case ExtrusionRole::erExternalPerimeter:
|
||||
return region->flow(FlowRole::frExternalPerimeter).scaled_width();
|
||||
case ExtrusionRole::erGapFill:
|
||||
return region->flow(FlowRole::frInfill).scaled_width();
|
||||
case ExtrusionRole::erPerimeter:
|
||||
return region->flow(FlowRole::frPerimeter).scaled_width();
|
||||
case ExtrusionRole::erSolidInfill:
|
||||
return region->flow(FlowRole::frSolidInfill).scaled_width();
|
||||
default:
|
||||
return region->flow(FlowRole::frExternalPerimeter).scaled_width();
|
||||
}
|
||||
}
|
||||
|
||||
Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, const Params ¶ms) {
|
||||
std::cout << "Checking: " << layer_idx << std::endl;
|
||||
if (layer_idx == 0) {
|
||||
// first layer is usually ok
|
||||
return {};
|
||||
}
|
||||
const Layer *layer = po->get_layer(layer_idx);
|
||||
EdgeGridWrapper supported_grid = compute_layer_edge_grid(layer->lower_layer);
|
||||
|
||||
Issues issues { };
|
||||
if (full_check) {
|
||||
for (const LayerRegion *layer_region : layer->regions()) {
|
||||
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
|
||||
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
|
||||
issues.add(check_extrusion_entity_stability(perimeter, layer_idx,
|
||||
layer->slice_z, get_flow_width(layer_region, perimeter->role()),
|
||||
supported_grid, params));
|
||||
} // perimeter
|
||||
} // ex_entity
|
||||
for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) {
|
||||
for (const ExtrusionEntity *fill : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
|
||||
if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) {
|
||||
issues.add(check_extrusion_entity_stability(fill, layer_idx,
|
||||
layer->slice_z, get_flow_width(layer_region, fill->role()),
|
||||
supported_grid, params));
|
||||
}
|
||||
} // fill
|
||||
} // ex_entity
|
||||
} // region
|
||||
} else { //check only external perimeters
|
||||
for (const LayerRegion *layer_region : layer->regions()) {
|
||||
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
|
||||
for (const ExtrusionEntity *perimeter : static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
|
||||
if (perimeter->role() == ExtrusionRole::erExternalPerimeter) {
|
||||
std::cout << "checking ex perimeter " << std::endl;
|
||||
issues.add(check_extrusion_entity_stability(perimeter, layer_idx,
|
||||
layer->slice_z, get_flow_width(layer_region, perimeter->role()),
|
||||
supported_grid, params));
|
||||
}; // ex_perimeter
|
||||
} // perimeter
|
||||
} // ex_entity
|
||||
} //region
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
} //Impl End
|
||||
|
||||
void quick_search(const PrintObject *po, const Params ¶ms = Params { }) {
|
||||
std::vector<size_t> quick_search(const PrintObject *po, const Params ¶ms) {
|
||||
using namespace Impl;
|
||||
std::vector<LayerDescriptor> descriptors(po->layer_count());
|
||||
|
||||
@ -129,20 +298,75 @@ void quick_search(const PrintObject *po, const Params ¶ms = Params { }) {
|
||||
} // thread
|
||||
);
|
||||
|
||||
for (size_t desc_idx = 0; desc_idx < descriptors.size(); ++desc_idx) {
|
||||
std::vector<size_t> suspicious_layers_indices { };
|
||||
|
||||
for (size_t desc_idx = 1; desc_idx < descriptors.size(); ++desc_idx) {
|
||||
const LayerDescriptor &prev = descriptors[desc_idx - 1];
|
||||
const LayerDescriptor &descriptor = descriptors[desc_idx];
|
||||
if (descriptor.segments_count - prev.segments_count != 0
|
||||
||
|
||||
std::abs(descriptor.perimeter_length - prev.perimeter_length)
|
||||
> params.perimeter_length_diff_tolerance ||
|
||||
(descriptor.centroid - prev.centroid).norm() > params.centroid_offset_tolerance
|
||||
) {
|
||||
suspicious_layers_indices.push_back(desc_idx);
|
||||
}
|
||||
#ifdef DEBUG_FILES
|
||||
std::cout << "SIS layer idx: " << desc_idx << " reg count: " << descriptor.segments_count << " len: "
|
||||
<< descriptor.perimeter_length <<
|
||||
" centroid: " << descriptor.centroid.x() << " | " << descriptor.centroid.y() << std::endl;
|
||||
if (desc_idx > 0) {
|
||||
const LayerDescriptor &prev = descriptors[desc_idx - 1];
|
||||
std::cout << "SIS diff: " << desc_idx << " reg count: "
|
||||
<< (int(descriptor.segments_count) - int(prev.segments_count)) <<
|
||||
" len: " << (descriptor.perimeter_length - prev.perimeter_length) <<
|
||||
" centroid: " << (descriptor.centroid - prev.centroid).norm() << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<bool> layer_needs_supports(suspicious_layers_indices.size(), false);
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, suspicious_layers_indices.size()),
|
||||
[&](tbb::blocked_range<size_t> r) {
|
||||
for (size_t suspicious_index = r.begin(); suspicious_index < r.end(); ++suspicious_index) {
|
||||
auto layer_issues = check_layer_stability(po, suspicious_layers_indices[suspicious_index],
|
||||
false,
|
||||
params);
|
||||
if (!layer_issues.supports_nedded.empty()) {
|
||||
layer_needs_supports[suspicious_index] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
std::vector<size_t> problematic_layers;
|
||||
|
||||
for (size_t index = suspicious_layers_indices.size() - 1; index <= 0; ++index) {
|
||||
if (!layer_needs_supports[index]) {
|
||||
problematic_layers.push_back(suspicious_layers_indices[index]);
|
||||
}
|
||||
}
|
||||
return problematic_layers;
|
||||
}
|
||||
|
||||
Issues full_search(const PrintObject *po, const Params ¶ms) {
|
||||
using namespace Impl;
|
||||
Issues issues { };
|
||||
for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) {
|
||||
auto layer_issues = check_layer_stability(po, layer_idx, true, params);
|
||||
if (!layer_issues.empty()) {
|
||||
issues.add(layer_issues);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_FILES
|
||||
Impl::debug_export(issues, "issues");
|
||||
#endif
|
||||
|
||||
// tbb::parallel_for(tbb::blocked_range<size_t>(0, suspicious_layers_indices.size()),
|
||||
// [&](tbb::blocked_range<size_t> r) {
|
||||
// for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
|
||||
// check_layer_stability(po, suspicious_layers_indices[layer_idx], params);
|
||||
// }
|
||||
// });
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,7 +7,25 @@ namespace Slic3r {
|
||||
|
||||
namespace SupportableIssues {
|
||||
|
||||
void quick_search(const PrintObject *po);
|
||||
struct Params {
|
||||
float bridge_distance = 5.0f;
|
||||
float limit_curvature = 0.25f;
|
||||
|
||||
float perimeter_length_diff_tolerance = 8.0f;
|
||||
float centroid_offset_tolerance = 1.0f;
|
||||
};
|
||||
|
||||
|
||||
struct Issues {
|
||||
std::vector<Vec3f> supports_nedded;
|
||||
std::vector<Vec3f> curling_up;
|
||||
|
||||
void add(const Issues &layer_issues);
|
||||
bool empty() const;
|
||||
};
|
||||
|
||||
std::vector<size_t> quick_search(const PrintObject *po, const Params ¶ms = Params { });
|
||||
Issues full_search(const PrintObject *po, const Params ¶ms = Params { });
|
||||
|
||||
}
|
||||
|
||||
|
1
src/libslic3r/TriangleSelectorWrapper.cpp
Normal file
1
src/libslic3r/TriangleSelectorWrapper.cpp
Normal file
@ -0,0 +1 @@
|
||||
|
42
src/libslic3r/TriangleSelectorWrapper.hpp
Normal file
42
src/libslic3r/TriangleSelectorWrapper.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_
|
||||
#define SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "TriangleSelector.hpp"
|
||||
#include "AABBTreeIndirect.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleSelectorWrapper {
|
||||
public:
|
||||
const TriangleMesh &mesh;
|
||||
TriangleSelector selector;
|
||||
AABBTreeIndirect::Tree<3, float> triangles_tree;
|
||||
|
||||
TriangleSelectorWrapper(const TriangleMesh &mesh) :
|
||||
mesh(mesh), selector(mesh), triangles_tree(AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices)) {
|
||||
|
||||
}
|
||||
|
||||
void enforce_spot(const Vec3f &point, float radius) {
|
||||
size_t hit_face_index;
|
||||
Vec3f hit_point;
|
||||
auto dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices,
|
||||
triangles_tree,
|
||||
point, hit_face_index, hit_point);
|
||||
if (dist < 0 || dist > radius)
|
||||
return;
|
||||
|
||||
std::unique_ptr<TriangleSelector::Cursor> cursor = std::make_unique<TriangleSelector::Sphere>(point, point,
|
||||
radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { });
|
||||
|
||||
selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), true,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_ */
|
Loading…
Reference in New Issue
Block a user