mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 23:15:54 +08:00
Autogenerate supportPoints by surface angle
This commit is contained in:
parent
63f6b293bf
commit
7a375abddb
@ -444,8 +444,6 @@ set(SLIC3R_SOURCES
|
|||||||
SLA/SupportPoint.hpp
|
SLA/SupportPoint.hpp
|
||||||
SLA/SupportPointGenerator.hpp
|
SLA/SupportPointGenerator.hpp
|
||||||
SLA/SupportPointGenerator.cpp
|
SLA/SupportPointGenerator.cpp
|
||||||
SLA/SupportPointGeneratorNew.hpp
|
|
||||||
SLA/SupportPointGeneratorNew.cpp
|
|
||||||
SLA/Clustering.hpp
|
SLA/Clustering.hpp
|
||||||
SLA/Clustering.cpp
|
SLA/Clustering.cpp
|
||||||
SLA/ReprojectPointsOnMesh.hpp
|
SLA/ReprojectPointsOnMesh.hpp
|
||||||
|
@ -1420,20 +1420,21 @@ namespace Slic3r {
|
|||||||
|
|
||||||
if (version == 0) {
|
if (version == 0) {
|
||||||
for (unsigned int i=0; i<object_data_points.size(); i+=3)
|
for (unsigned int i=0; i<object_data_points.size(); i+=3)
|
||||||
sla_support_points.emplace_back(float(std::atof(object_data_points[i+0].c_str())),
|
sla_support_points.push_back(sla::SupportPoint{Vec3f(
|
||||||
|
float(std::atof(object_data_points[i+0].c_str())),
|
||||||
float(std::atof(object_data_points[i+1].c_str())),
|
float(std::atof(object_data_points[i+1].c_str())),
|
||||||
float(std::atof(object_data_points[i+2].c_str())),
|
float(std::atof(object_data_points[i+2].c_str()))),
|
||||||
0.4f,
|
0.4f});
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
if (version == 1) {
|
if (version == 1) {
|
||||||
for (unsigned int i=0; i<object_data_points.size(); i+=5)
|
for (unsigned int i=0; i<object_data_points.size(); i+=5)
|
||||||
sla_support_points.emplace_back(float(std::atof(object_data_points[i+0].c_str())),
|
sla_support_points.push_back(sla::SupportPoint{Vec3f(
|
||||||
|
float(std::atof(object_data_points[i+0].c_str())),
|
||||||
float(std::atof(object_data_points[i+1].c_str())),
|
float(std::atof(object_data_points[i+1].c_str())),
|
||||||
float(std::atof(object_data_points[i+2].c_str())),
|
float(std::atof(object_data_points[i+2].c_str()))),
|
||||||
float(std::atof(object_data_points[i+3].c_str())),
|
float(std::atof(object_data_points[i+3].c_str()))});
|
||||||
//FIXME storing boolean as 0 / 1 and importing it as float.
|
//FIXME storing boolean as 0 / 1 and importing it as float.
|
||||||
std::abs(std::atof(object_data_points[i+4].c_str()) - 1.) < EPSILON);
|
//std::abs(std::atof(object_data_points[i+4].c_str()) - 1.) < EPSILON);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sla_support_points.empty())
|
if (!sla_support_points.empty())
|
||||||
@ -3542,7 +3543,7 @@ namespace Slic3r {
|
|||||||
|
|
||||||
// Store the layer height profile as a single space separated list.
|
// Store the layer height profile as a single space separated list.
|
||||||
for (size_t i = 0; i < sla_support_points.size(); ++i) {
|
for (size_t i = 0; i < sla_support_points.size(); ++i) {
|
||||||
sprintf(buffer, (i==0 ? "%f %f %f %f %f" : " %f %f %f %f %f"), sla_support_points[i].pos(0), sla_support_points[i].pos(1), sla_support_points[i].pos(2), sla_support_points[i].head_front_radius, (float)sla_support_points[i].is_new_island);
|
sprintf(buffer, (i==0 ? "%f %f %f %f %f" : " %f %f %f %f %f"), sla_support_points[i].pos(0), sla_support_points[i].pos(1), sla_support_points[i].pos(2), sla_support_points[i].head_front_radius, (float)(sla_support_points[i].is_island()));
|
||||||
out += buffer;
|
out += buffer;
|
||||||
}
|
}
|
||||||
out += "\n";
|
out += "\n";
|
||||||
|
@ -774,7 +774,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||||||
|
|
||||||
point(coord_idx) = float(atof(p));
|
point(coord_idx) = float(atof(p));
|
||||||
if (++coord_idx == 5) {
|
if (++coord_idx == 5) {
|
||||||
m_object->sla_support_points.push_back(sla::SupportPoint(point));
|
m_object->sla_support_points.push_back(sla::SupportPoint{Vec3f(point[0], point[1], point[2]), point[3]});
|
||||||
coord_idx = 0;
|
coord_idx = 0;
|
||||||
}
|
}
|
||||||
if (end == nullptr)
|
if (end == nullptr)
|
||||||
|
@ -7,11 +7,7 @@
|
|||||||
|
|
||||||
#include <libslic3r/Point.hpp>
|
#include <libslic3r/Point.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r::sla {
|
||||||
|
|
||||||
class ModelObject;
|
|
||||||
|
|
||||||
namespace sla {
|
|
||||||
|
|
||||||
// An enum to keep track of where the current points on the ModelObject came from.
|
// An enum to keep track of where the current points on the ModelObject came from.
|
||||||
enum class PointsStatus {
|
enum class PointsStatus {
|
||||||
@ -21,58 +17,47 @@ enum class PointsStatus {
|
|||||||
UserModified // User has done some edits.
|
UserModified // User has done some edits.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Reason of automatic support placement usage
|
||||||
|
enum class SupportPointType {
|
||||||
|
manual_add,
|
||||||
|
island, // no move, island should be grouped
|
||||||
|
slope,
|
||||||
|
thin,
|
||||||
|
stability,
|
||||||
|
edge
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stereolithography(SLA) support point
|
||||||
|
/// </summary>
|
||||||
struct SupportPoint
|
struct SupportPoint
|
||||||
{
|
{
|
||||||
Vec3f pos;
|
// Position on model surface
|
||||||
float head_front_radius;
|
Vec3f pos = Vec3f::Zero(); // [in mm]
|
||||||
bool is_new_island;
|
|
||||||
|
|
||||||
SupportPoint()
|
// radius of the touching interface
|
||||||
: pos(Vec3f::Zero()), head_front_radius(0.f), is_new_island(false)
|
// Also define force it must keep
|
||||||
{}
|
float head_front_radius = 0.f; // [in mm]
|
||||||
|
|
||||||
SupportPoint(float pos_x,
|
// type
|
||||||
float pos_y,
|
SupportPointType type{SupportPointType::manual_add};
|
||||||
float pos_z,
|
|
||||||
float head_radius,
|
|
||||||
bool new_island = false)
|
|
||||||
: pos(pos_x, pos_y, pos_z)
|
|
||||||
, head_front_radius(head_radius)
|
|
||||||
, is_new_island(new_island)
|
|
||||||
{}
|
|
||||||
|
|
||||||
SupportPoint(Vec3f position, float head_radius, bool new_island = false)
|
bool is_island() const { return type == SupportPointType::island; }
|
||||||
: pos(position)
|
template<class Archive> void serialize(Archive &ar){
|
||||||
, head_front_radius(head_radius)
|
ar(pos, head_front_radius, type);
|
||||||
, is_new_island(new_island)
|
}
|
||||||
{}
|
|
||||||
|
|
||||||
SupportPoint(Eigen::Matrix<float, 5, 1, Eigen::DontAlign> data)
|
// unsaved changes + cache invalidation
|
||||||
: pos(data(0), data(1), data(2))
|
bool operator==(const SupportPoint &sp) const {
|
||||||
, head_front_radius(data(3))
|
|
||||||
, is_new_island(data(4) != 0.f)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool operator==(const SupportPoint &sp) const
|
|
||||||
{
|
|
||||||
float rdiff = std::abs(head_front_radius - sp.head_front_radius);
|
float rdiff = std::abs(head_front_radius - sp.head_front_radius);
|
||||||
return (pos == sp.pos) && rdiff < float(EPSILON) &&
|
return (pos == sp.pos) && rdiff < float(EPSILON) && type == sp.type;
|
||||||
is_new_island == sp.is_new_island;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(const SupportPoint &sp) const { return !(sp == (*this)); }
|
bool operator!=(const SupportPoint &sp) const { return !(sp == (*this)); }
|
||||||
|
|
||||||
template<class Archive> void serialize(Archive &ar)
|
|
||||||
{
|
|
||||||
ar(pos, head_front_radius, is_new_island);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using SupportPoints = std::vector<SupportPoint>;
|
using SupportPoints = std::vector<SupportPoint>;
|
||||||
|
|
||||||
SupportPoints transformed_support_points(const ModelObject &mo,
|
} // namespace Slic3r::sla
|
||||||
const Transform3d &trafo);
|
|
||||||
|
|
||||||
}} // namespace Slic3r::sla
|
|
||||||
|
|
||||||
#endif // SUPPORTPOINT_HPP
|
#endif // SUPPORTPOINT_HPP
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,262 +1,191 @@
|
|||||||
///|/ Copyright (c) Prusa Research 2020 - 2022 Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena
|
///|/ Copyright (c) Prusa Research 2024 Filip Sykala @Jony01
|
||||||
///|/
|
///|/
|
||||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||||
///|/
|
///|/
|
||||||
#ifndef SLA_SUPPORTPOINTGENERATOR_HPP
|
#ifndef SLA_SUPPORTPOINTGENERATOR_HPP
|
||||||
#define SLA_SUPPORTPOINTGENERATOR_HPP
|
#define SLA_SUPPORTPOINTGENERATOR_HPP
|
||||||
|
|
||||||
#include <libslic3r/AABBMesh.hpp>
|
|
||||||
#include <libslic3r/SLA/SupportPoint.hpp>
|
|
||||||
#include <libslic3r/BoundingBox.hpp>
|
|
||||||
#include <libslic3r/ClipperUtils.hpp>
|
|
||||||
#include <libslic3r/Point.hpp>
|
|
||||||
#include <boost/container/small_vector.hpp>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <random>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <functional>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cinttypes>
|
#include <functional>
|
||||||
|
|
||||||
|
#include <boost/container/small_vector.hpp>
|
||||||
|
|
||||||
|
#include "libslic3r/Point.hpp"
|
||||||
#include "libslic3r/ExPolygon.hpp"
|
#include "libslic3r/ExPolygon.hpp"
|
||||||
#include "libslic3r/Polygon.hpp"
|
#include "libslic3r/SLA/SupportPoint.hpp"
|
||||||
#include "libslic3r/libslic3r.h"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r::sla {
|
||||||
class AABBMesh;
|
|
||||||
} // namespace Slic3r
|
|
||||||
|
|
||||||
// #define SLA_SUPPORTPOINTGEN_DEBUG
|
/// <summary>
|
||||||
|
/// Configuration for automatic support placement
|
||||||
|
/// </summary>
|
||||||
|
struct SupportPointGeneratorConfig{
|
||||||
|
/// <summary>
|
||||||
|
/// 0 mean only one support point for each island
|
||||||
|
/// lower than one mean less amount of support points
|
||||||
|
/// 1 mean fine tuned sampling
|
||||||
|
/// more than one mean bigger amout of support points
|
||||||
|
/// </summary>
|
||||||
|
float density_relative{1.f};
|
||||||
|
|
||||||
namespace Slic3r { namespace sla {
|
/// <summary>
|
||||||
|
/// Size range for support point interface (head)
|
||||||
|
/// </summary>
|
||||||
|
MinMax<float> head_diameter = {0.2f, 0.6f}; // [in mm]
|
||||||
|
|
||||||
class SupportPointGenerator {
|
// FIXME: calculate actual pixel area from printer config:
|
||||||
public:
|
// const float pixel_area =
|
||||||
struct Config final {
|
// pow(wxGetApp().preset_bundle->project_config.option<ConfigOptionFloat>("display_width") /
|
||||||
float density_relative {1.f};
|
// wxGetApp().preset_bundle->project_config.option<ConfigOptionInt>("display_pixels_x"), 2.f); //
|
||||||
float minimal_distance {1.f};
|
// Minimal island Area to print - TODO: Should be modifiable from UI
|
||||||
float head_diameter {0.4f};
|
// !! Filter should be out of sampling algorithm !!
|
||||||
|
float minimal_island_area = pow(0.047f, 2.f); // [in mm^2] pixel_area
|
||||||
// Originally calibrated to 7.7f, reduced density by Tamas to 70% which is 11.1 (7.7 / 0.7) to adjust for new algorithm changes in tm_suppt_gen_improve
|
|
||||||
inline float support_force() const { return 11.1f / density_relative; } // a force one point can support (arbitrary force unit)
|
|
||||||
|
|
||||||
// FIXME: calculate actual pixel area from printer config:
|
|
||||||
//const float pixel_area = pow(wxGetApp().preset_bundle->project_config.option<ConfigOptionFloat>("display_width") / wxGetApp().preset_bundle->project_config.option<ConfigOptionInt>("display_pixels_x"), 2.f); //
|
|
||||||
// Minimal island Area to print - TODO: Should be modifiable from UI
|
|
||||||
const float minimal_island_area = pow(0.047f, 2.f); // [in mm^2] pixel_area
|
|
||||||
};
|
|
||||||
|
|
||||||
SupportPointGenerator(const AABBMesh& emesh, const std::vector<ExPolygons>& slices,
|
|
||||||
const std::vector<float>& heights, const Config& config, std::function<void(void)> throw_on_cancel, std::function<void(int)> statusfn);
|
|
||||||
|
|
||||||
SupportPointGenerator(const AABBMesh& emesh, const Config& config, std::function<void(void)> throw_on_cancel, std::function<void(int)> statusfn);
|
|
||||||
|
|
||||||
const std::vector<SupportPoint>& output() const { return m_output; }
|
|
||||||
std::vector<SupportPoint>& output() { return m_output; }
|
|
||||||
|
|
||||||
struct MyLayer;
|
|
||||||
|
|
||||||
// Keep data for one area(ExPlygon) on the layer(on slice Expolygons)
|
|
||||||
struct Structure {
|
|
||||||
Structure(MyLayer &layer, const ExPolygon& poly, const BoundingBox &bbox, float area) :
|
|
||||||
layer(&layer), polygon(&poly), bbox(bbox), area(area)
|
|
||||||
#ifdef SLA_SUPPORTPOINTGEN_DEBUG
|
|
||||||
, unique_id(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()))
|
|
||||||
#endif /* SLA_SUPPORTPOINTGEN_DEBUG */
|
|
||||||
{}
|
|
||||||
// Parent layer - with all ExPolygons in layer + layer_height
|
|
||||||
MyLayer *layer;
|
|
||||||
// Source ExPolygon
|
|
||||||
const ExPolygon* polygon = nullptr;
|
|
||||||
// Cache bounding box of polygon
|
|
||||||
const BoundingBox bbox;
|
|
||||||
// area of polygon [in mm^2] without holes
|
|
||||||
const float area = 0.f;
|
|
||||||
|
|
||||||
// How well is this ExPolygon held to the print base?
|
|
||||||
// Positive number, the higher the better.
|
|
||||||
float supports_force_this_layer = 0.f;
|
|
||||||
float supports_force_inherited = 0.f;
|
|
||||||
float supports_force_total() const { return this->supports_force_this_layer + this->supports_force_inherited; }
|
|
||||||
#ifdef SLA_SUPPORTPOINTGEN_DEBUG
|
|
||||||
std::chrono::milliseconds unique_id;
|
|
||||||
#endif /* SLA_SUPPORTPOINTGEN_DEBUG */
|
|
||||||
|
|
||||||
struct Link {
|
|
||||||
Link(Structure *island, float overlap_area) : island(island), overlap_area(overlap_area) {}
|
|
||||||
Structure *island;
|
|
||||||
float overlap_area;
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef NDEBUG
|
|
||||||
// In release mode, use the optimized container.
|
|
||||||
boost::container::small_vector<Link, 4> islands_above;
|
|
||||||
boost::container::small_vector<Link, 4> islands_below;
|
|
||||||
#else
|
|
||||||
// In debug mode, use the standard vector, which is well handled by debugger visualizer.
|
|
||||||
std::vector<Link> islands_above;
|
|
||||||
std::vector<Link> islands_below;
|
|
||||||
#endif
|
|
||||||
// Overhangs, that are dangling considerably.
|
|
||||||
ExPolygons dangling_areas;
|
|
||||||
// Complete overhangs.
|
|
||||||
ExPolygons overhangs;
|
|
||||||
// Overhangs, where the surface must slope.
|
|
||||||
ExPolygons overhangs_slopes;
|
|
||||||
// Sum of all overhang areas from structure
|
|
||||||
float overhangs_area = 0.f; // [in mm^2]
|
|
||||||
|
|
||||||
bool overlaps(const Structure &rhs) const {
|
|
||||||
return this->bbox.overlap(rhs.bbox) && this->polygon->overlaps(*rhs.polygon);
|
|
||||||
}
|
|
||||||
float overlap_area(const Structure &rhs) const {
|
|
||||||
double out = 0.;
|
|
||||||
if (this->bbox.overlap(rhs.bbox)) {
|
|
||||||
Polygons polys = intersection(*this->polygon, *rhs.polygon);
|
|
||||||
for (const Polygon &poly : polys)
|
|
||||||
out += poly.area();
|
|
||||||
}
|
|
||||||
return float(out);
|
|
||||||
}
|
|
||||||
float area_below() const {
|
|
||||||
float area = 0.f;
|
|
||||||
for (const Link &below : this->islands_below)
|
|
||||||
area += below.island->area;
|
|
||||||
return area;
|
|
||||||
}
|
|
||||||
Polygons polygons_below() const {
|
|
||||||
size_t cnt = 0;
|
|
||||||
for (const Link &below : this->islands_below)
|
|
||||||
cnt += 1 + below.island->polygon->holes.size();
|
|
||||||
Polygons out;
|
|
||||||
out.reserve(cnt);
|
|
||||||
for (const Link &below : this->islands_below) {
|
|
||||||
out.emplace_back(below.island->polygon->contour);
|
|
||||||
append(out, below.island->polygon->holes);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
ExPolygons expolygons_below() const {
|
|
||||||
ExPolygons out;
|
|
||||||
out.reserve(this->islands_below.size());
|
|
||||||
for (const Link &below : this->islands_below)
|
|
||||||
out.emplace_back(*below.island->polygon);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
// Positive deficit of the supports. If negative, this area is well supported. If positive, more supports need to be added.
|
|
||||||
float support_force_deficit(const float tear_pressure) const { return this->area * tear_pressure - this->supports_force_total(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct MyLayer {
|
|
||||||
MyLayer(const size_t layer_id, coordf_t print_z) : layer_id(layer_id), print_z(print_z) {}
|
|
||||||
// index into heights + slices
|
|
||||||
size_t layer_id;
|
|
||||||
// Absolute distance from Zero - copy value from heights<float>
|
|
||||||
coordf_t print_z; // [in mm]
|
|
||||||
std::vector<Structure> islands;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RichSupportPoint {
|
|
||||||
Vec3f position;
|
|
||||||
Structure *island;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PointGrid3D {
|
|
||||||
struct GridHash {
|
|
||||||
std::size_t operator()(const Vec3i &cell_id) const {
|
|
||||||
return std::hash<int>()(cell_id.x()) ^ std::hash<int>()(cell_id.y() * 593) ^ std::hash<int>()(cell_id.z() * 7919);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
typedef std::unordered_multimap<Vec3i, RichSupportPoint, GridHash> Grid;
|
|
||||||
|
|
||||||
Vec3f cell_size;
|
|
||||||
Grid grid;
|
|
||||||
|
|
||||||
Vec3i cell_id(const Vec3f &pos) {
|
|
||||||
return Vec3i(int(floor(pos.x() / cell_size.x())),
|
|
||||||
int(floor(pos.y() / cell_size.y())),
|
|
||||||
int(floor(pos.z() / cell_size.z())));
|
|
||||||
}
|
|
||||||
|
|
||||||
void insert(const Vec2f &pos, Structure *island) {
|
|
||||||
RichSupportPoint pt;
|
|
||||||
pt.position = Vec3f(pos.x(), pos.y(), float(island->layer->print_z));
|
|
||||||
pt.island = island;
|
|
||||||
grid.emplace(cell_id(pt.position), pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool collides_with(const Vec2f &pos, float print_z, float radius) {
|
|
||||||
Vec3f pos3d(pos.x(), pos.y(), print_z);
|
|
||||||
Vec3i cell = cell_id(pos3d);
|
|
||||||
std::pair<Grid::const_iterator, Grid::const_iterator> it_pair = grid.equal_range(cell);
|
|
||||||
if (collides_with(pos3d, radius, it_pair.first, it_pair.second))
|
|
||||||
return true;
|
|
||||||
for (int i = -1; i < 2; ++ i)
|
|
||||||
for (int j = -1; j < 2; ++ j)
|
|
||||||
for (int k = -1; k < 1; ++ k) {
|
|
||||||
if (i == 0 && j == 0 && k == 0)
|
|
||||||
continue;
|
|
||||||
it_pair = grid.equal_range(cell + Vec3i(i, j, k));
|
|
||||||
if (collides_with(pos3d, radius, it_pair.first, it_pair.second))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool collides_with(const Vec3f &pos, float radius, Grid::const_iterator it_begin, Grid::const_iterator it_end) {
|
|
||||||
for (Grid::const_iterator it = it_begin; it != it_end; ++ it) {
|
|
||||||
float dist2 = (it->second.position - pos).squaredNorm();
|
|
||||||
if (dist2 < radius * radius)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void execute(const std::vector<ExPolygons> &slices,
|
|
||||||
const std::vector<float> & heights);
|
|
||||||
|
|
||||||
void seed(std::mt19937::result_type s) { m_rng.seed(s); }
|
|
||||||
private:
|
|
||||||
std::vector<SupportPoint> m_output;
|
|
||||||
|
|
||||||
// Configuration
|
|
||||||
SupportPointGenerator::Config m_config;
|
|
||||||
|
|
||||||
void process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights);
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum IslandCoverageFlags : uint8_t { icfNone = 0x0, icfIsNew = 0x1, icfWithBoundary = 0x2 };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void uniformly_cover(const ExPolygons& islands, Structure& structure, float deficit, PointGrid3D &grid3d, IslandCoverageFlags flags = icfNone);
|
|
||||||
|
|
||||||
void add_support_points(Structure& structure, PointGrid3D &grid3d);
|
|
||||||
|
|
||||||
void project_onto_mesh(std::vector<SupportPoint>& points) const;
|
|
||||||
|
|
||||||
#ifdef SLA_SUPPORTPOINTGEN_DEBUG
|
|
||||||
static void output_expolygons(const ExPolygons& expolys, const std::string &filename);
|
|
||||||
static void output_structures(const std::vector<Structure> &structures);
|
|
||||||
#endif // SLA_SUPPORTPOINTGEN_DEBUG
|
|
||||||
|
|
||||||
const AABBMesh& m_emesh;
|
|
||||||
std::function<void(void)> m_throw_on_cancel;
|
|
||||||
std::function<void(int)> m_statusfn;
|
|
||||||
|
|
||||||
std::mt19937 m_rng;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void remove_bottom_points(std::vector<SupportPoint> &pts, float lvl);
|
struct LayerPart; // forward decl.
|
||||||
|
using LayerParts = std::vector<LayerPart>;
|
||||||
|
|
||||||
std::vector<Vec2f> sample_expolygon(const ExPolygon &expoly, float samples_per_mm2, std::mt19937 &rng);
|
struct PartLink
|
||||||
void sample_expolygon_boundary(const ExPolygon &expoly, float samples_per_mm, std::vector<Vec2f> &out, std::mt19937 &rng);
|
{
|
||||||
|
LayerParts::const_iterator part_it;
|
||||||
|
// float overlap_area; // sum of overlap areas
|
||||||
|
// ExPolygons overlap; // clipper intersection_ex
|
||||||
|
// ExPolygons overhang; // clipper diff_ex
|
||||||
|
};
|
||||||
|
#ifdef NDEBUG
|
||||||
|
// In release mode, use the optimized container.
|
||||||
|
using PartLinks = boost::container::small_vector<PartLink, 4>;
|
||||||
|
#else
|
||||||
|
// In debug mode, use the standard vector, which is well handled by debugger visualizer.
|
||||||
|
using PartLinks = std::vector<PartLink>;
|
||||||
|
#endif
|
||||||
|
|
||||||
}} // namespace Slic3r::sla
|
// Part on layer is defined by its shape
|
||||||
|
struct LayerPart {
|
||||||
|
// Pointer to expolygon stored in input
|
||||||
|
const ExPolygon *shape;
|
||||||
|
|
||||||
#endif // SUPPORTPOINTGENERATOR_HPP
|
// rectangular bounding box of shape
|
||||||
|
BoundingBox shape_extent;
|
||||||
|
|
||||||
|
// uniformly sampled shape contour
|
||||||
|
Slic3r::Points samples;
|
||||||
|
// IMPROVE: sample only overhangs part of shape
|
||||||
|
|
||||||
|
// Parts from previous printed layer, which is connected to current part
|
||||||
|
PartLinks prev_parts;
|
||||||
|
PartLinks next_parts;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extend support point with information from layer
|
||||||
|
/// </summary>
|
||||||
|
struct LayerSupportPoint: public SupportPoint
|
||||||
|
{
|
||||||
|
// Pointer on source ExPolygon otherwise nullptr
|
||||||
|
//const LayerPart *part{nullptr};
|
||||||
|
|
||||||
|
// 2d coordinate on layer
|
||||||
|
// use only when part is not nullptr
|
||||||
|
Point position_on_layer; // [scaled_ unit]
|
||||||
|
|
||||||
|
// 2d direction into expolygon mass
|
||||||
|
// used as ray to positioning 3d point on mesh surface
|
||||||
|
// Island has direction [0,0] - should be placed on surface from bottom
|
||||||
|
Point direction_to_mass;
|
||||||
|
};
|
||||||
|
using LayerSupportPoints = std::vector<LayerSupportPoint>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// One slice divided into
|
||||||
|
/// </summary>
|
||||||
|
struct Layer
|
||||||
|
{
|
||||||
|
// index into parent Layesr + heights + slices
|
||||||
|
// [[deprecated]] Use index to layers insted of adress from item
|
||||||
|
size_t layer_id;
|
||||||
|
|
||||||
|
// Absolute distance from Zero - copy value from heights<float>
|
||||||
|
// [[deprecated]] Use index to layers insted of adress from item
|
||||||
|
double print_z; // [in mm]
|
||||||
|
|
||||||
|
// data for one expolygon
|
||||||
|
LayerParts parts;
|
||||||
|
};
|
||||||
|
using Layers = std::vector<Layer>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keep state of Support Point generation
|
||||||
|
/// Used for resampling with different configuration
|
||||||
|
/// </summary>
|
||||||
|
struct SupportPointGeneratorData
|
||||||
|
{
|
||||||
|
// Input slices of mesh
|
||||||
|
std::vector<ExPolygons> slices;
|
||||||
|
// Same size as slices
|
||||||
|
std::vector<float> heights;
|
||||||
|
|
||||||
|
// link to slices
|
||||||
|
Layers layers;
|
||||||
|
};
|
||||||
|
|
||||||
|
// call during generation of support points to check cancel event
|
||||||
|
using ThrowOnCancel = std::function<void(void)>;
|
||||||
|
// call to say progress of generation into gui in range from 0 to 100
|
||||||
|
using StatusFunction= std::function<void(int)>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prepare data for generate support points
|
||||||
|
/// Used for interactive resampling to store permanent data between configuration changes.,
|
||||||
|
/// Everything which could be prepared are stored into result.
|
||||||
|
/// Need to regenerate on mesh change(Should be connected with ObjectId) OR change of slicing heights
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slices">Countour cut from mesh</param>
|
||||||
|
/// <param name="heights">Heights of the slices - Same size as slices</param>
|
||||||
|
/// <param name="throw_on_cancel">Call in meanwhile to check cancel event</param>
|
||||||
|
/// <param name="statusfn">Say progress of generation into gui</param>
|
||||||
|
/// <returns>Data prepared for generate support points</returns>
|
||||||
|
SupportPointGeneratorData prepare_generator_data(
|
||||||
|
std::vector<ExPolygons> &&slices,
|
||||||
|
std::vector<float> &&heights,
|
||||||
|
ThrowOnCancel throw_on_cancel,
|
||||||
|
StatusFunction statusfn
|
||||||
|
);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate support points on islands by configuration parameters
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">Preprocessed data needed for sampling</param>
|
||||||
|
/// <param name="config">Define density of samples</param>
|
||||||
|
/// <param name="throw_on_cancel">Call in meanwhile to check cancel event</param>
|
||||||
|
/// <param name="statusfn">Progress of generation into gui</param>
|
||||||
|
/// <returns>Generated support points</returns>
|
||||||
|
LayerSupportPoints generate_support_points(
|
||||||
|
const SupportPointGeneratorData &data,
|
||||||
|
const SupportPointGeneratorConfig &config,
|
||||||
|
ThrowOnCancel throw_on_cancel,
|
||||||
|
StatusFunction statusfn
|
||||||
|
);
|
||||||
|
} // namespace Slic3r::sla
|
||||||
|
|
||||||
|
// TODO: Not sure if it is neccessary & Should be in another file
|
||||||
|
namespace Slic3r{
|
||||||
|
class AABBMesh;
|
||||||
|
namespace sla {
|
||||||
|
/// <summary>
|
||||||
|
/// Move support points on surface of mesh
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="points">Support points to move on surface</param>
|
||||||
|
/// <param name="mesh">Define surface for move points</param>
|
||||||
|
/// <param name="throw_on_cancel">Call in meanwhile to check cancel event</param>
|
||||||
|
/// <returns>Support points laying on mesh surface</returns>
|
||||||
|
SupportPoints move_on_mesh_surface(
|
||||||
|
const LayerSupportPoints &points,
|
||||||
|
const AABBMesh &mesh,
|
||||||
|
double allowed_move,
|
||||||
|
ThrowOnCancel throw_on_cancel
|
||||||
|
);
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
#endif // SLA_SUPPORTPOINTGENERATOR_HPP
|
||||||
|
@ -1,382 +0,0 @@
|
|||||||
///|/ Copyright (c) Prusa Research 2024 Filip Sykala @Jony01
|
|
||||||
///|/
|
|
||||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
|
||||||
///|/
|
|
||||||
|
|
||||||
#include "SupportPointGeneratorNew.hpp"
|
|
||||||
|
|
||||||
#include <unordered_map> // point grid
|
|
||||||
|
|
||||||
#include "libslic3r/Execution/ExecutionTBB.hpp" // parallel preparation of data for sampling
|
|
||||||
#include "libslic3r/Execution/Execution.hpp"
|
|
||||||
|
|
||||||
using namespace Slic3r;
|
|
||||||
using namespace Slic3r::sla;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Struct to store support points in 2d grid to faster search for nearest support points
|
|
||||||
/// </summary>
|
|
||||||
class Grid2D
|
|
||||||
{
|
|
||||||
coord_t m_cell_size; // Squar: x and y are same
|
|
||||||
coord_t m_cell_size_half;
|
|
||||||
using Key = Point;
|
|
||||||
using Grid = std::unordered_multimap<Key, SupportPoint>;
|
|
||||||
Grid m_grid;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// <summary>
|
|
||||||
/// Set cell size for grid
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cell_size">Granularity of stored points
|
|
||||||
/// Must be bigger than maximal used radius</param>
|
|
||||||
explicit Grid2D(const coord_t &cell_size)
|
|
||||||
: m_cell_size(cell_size), m_cell_size_half(cell_size / 2) {}
|
|
||||||
|
|
||||||
Key cell_id(const Point &point) const {
|
|
||||||
return Key(point.x() / m_cell_size, point.y() / m_cell_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add(SupportPoint &&point) {
|
|
||||||
m_grid.emplace(cell_id(point.position_on_layer), std::move(point));
|
|
||||||
}
|
|
||||||
|
|
||||||
using CheckFnc = std::function<bool(const SupportPoint &, const Point&)>;
|
|
||||||
bool exist_true_in_4cell_neighbor(const Point &pos, const CheckFnc& fnc) const {
|
|
||||||
Key key = cell_id(pos);
|
|
||||||
if (exist_true_for_cell(key, pos, fnc)) return true;
|
|
||||||
Point un_cell_pos(
|
|
||||||
key.x() * m_cell_size + m_cell_size_half,
|
|
||||||
key.y() * m_cell_size + m_cell_size_half );
|
|
||||||
Key key2(
|
|
||||||
(un_cell_pos.x() > pos.x()) ? key.x() + 1 : key.x() - 1,
|
|
||||||
(un_cell_pos.y() > pos.y()) ? key.y() + 1 : key.y() - 1);
|
|
||||||
if (exist_true_for_cell(key2, pos, fnc)) return true;
|
|
||||||
if (exist_true_for_cell({key.x(), key2.y()}, pos, fnc)) return true;
|
|
||||||
if (exist_true_for_cell({key2.x(), key.y()}, pos, fnc)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void merge(Grid2D &&grid) {
|
|
||||||
// support to merge only grid with same size
|
|
||||||
assert(m_cell_size == grid.m_cell_size);
|
|
||||||
m_grid.merge(std::move(grid.m_grid));
|
|
||||||
}
|
|
||||||
|
|
||||||
SupportPoints get_points() const {
|
|
||||||
SupportPoints result;
|
|
||||||
result.reserve(m_grid.size());
|
|
||||||
for (const auto& [key, support] : m_grid)
|
|
||||||
result.push_back(support);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
bool exist_true_for_cell(const Key &key, const Point &pos, const CheckFnc& fnc) const{
|
|
||||||
auto [begin_it, end_it] = m_grid.equal_range(key);
|
|
||||||
for (Grid::const_iterator it = begin_it; it != end_it; ++it) {
|
|
||||||
const SupportPoint &support_point = it->second;
|
|
||||||
if (fnc(support_point, pos))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Intersection of line segment and circle
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="p1">Line segment point A, Point lay inside circle</param>
|
|
||||||
/// <param name="p2">Line segment point B, Point lay outside or on circle</param>
|
|
||||||
/// <param name="cnt">Circle center point</param>
|
|
||||||
/// <param name="r2">squared value of Circle Radius (r2 = r*r)</param>
|
|
||||||
/// <returns>Intersection point</returns>
|
|
||||||
Point intersection(const Point &p1, const Point &p2, const Point &cnt, double r2) {
|
|
||||||
// Vector from p1 to p2
|
|
||||||
Vec2d dp_d((p2 - p1).cast<double>());
|
|
||||||
// Vector from circle center to p1
|
|
||||||
Vec2d f_d((p1 - cnt).cast<double>());
|
|
||||||
|
|
||||||
double a = dp_d.squaredNorm();
|
|
||||||
double b = 2 * (f_d.x() * dp_d.x() + f_d.y() * dp_d.y());
|
|
||||||
double c = f_d.squaredNorm() - r2;
|
|
||||||
|
|
||||||
// Discriminant of the quadratic equation
|
|
||||||
double discriminant = b * b - 4 * a * c;
|
|
||||||
|
|
||||||
// No intersection if discriminant is negative
|
|
||||||
assert(discriminant > 0);
|
|
||||||
if (discriminant < 0)
|
|
||||||
return {}; // No intersection
|
|
||||||
|
|
||||||
// Calculate the two possible values of t (parametric parameter)
|
|
||||||
discriminant = sqrt(discriminant);
|
|
||||||
double t1 = (-b - discriminant) / (2 * a);
|
|
||||||
|
|
||||||
// Check for valid intersection points within the line segment
|
|
||||||
if (t1 >= 0 && t1 <= 1) {
|
|
||||||
return {p1.x() + t1 * dp_d.x(), p1.y() + t1 * dp_d.y()};
|
|
||||||
}
|
|
||||||
|
|
||||||
// should not be in use
|
|
||||||
double t2 = (-b + discriminant) / (2 * a);
|
|
||||||
if (t2 >= 0 && t2 <= 1 && t1 != t2) {
|
|
||||||
return {p1.x() + t2 * dp_d.x(), p1.y() + t2 * dp_d.y()};
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Uniformly sample Polygon,
|
|
||||||
/// Use first point and each next point is first crosing radius from last added
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="p">Polygon to sample</param>
|
|
||||||
/// <param name="dist2">Squared distance for sampling</param>
|
|
||||||
/// <returns>Uniformly distributed points laying on input polygon
|
|
||||||
/// with exception of first and last point(they are closer than dist2)</returns>
|
|
||||||
Slic3r::Points sample(const Polygon &p, double dist2) {
|
|
||||||
if (p.empty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
Slic3r::Points r;
|
|
||||||
r.push_back(p.front());
|
|
||||||
const Point *prev_pt = nullptr;
|
|
||||||
for (size_t prev_i = 0; prev_i < p.size(); prev_i++) {
|
|
||||||
size_t curr_i = (prev_i != p.size() - 1) ? prev_i + 1 : 0;
|
|
||||||
const Point &pt = p.points[curr_i];
|
|
||||||
double p_dist2 = (r.back() - pt).cast<double>().squaredNorm();
|
|
||||||
while (p_dist2 > dist2) { // line segment goes out of radius
|
|
||||||
if (prev_pt == nullptr)
|
|
||||||
prev_pt = &p.points[prev_i];
|
|
||||||
r.push_back(intersection(*prev_pt, pt, r.back(), dist2));
|
|
||||||
p_dist2 = (r.back() - pt).cast<double>().squaredNorm();
|
|
||||||
prev_pt = &r.back();
|
|
||||||
}
|
|
||||||
prev_pt = nullptr;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
coord_t get_supported_radius(const SupportPoint &p, float z_distance, const SupportPointGeneratorConfig &config
|
|
||||||
) {
|
|
||||||
// TODO: calculate support radius
|
|
||||||
return scale_(5.);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sample_part(
|
|
||||||
const LayerPart &part,
|
|
||||||
size_t layer_id,
|
|
||||||
const SupportPointGeneratorData &data,
|
|
||||||
const SupportPointGeneratorConfig &config,
|
|
||||||
std::vector<Grid2D> &grids,
|
|
||||||
std::vector<Grid2D> &prev_grids
|
|
||||||
) {
|
|
||||||
// NOTE: first layer do not have prev part
|
|
||||||
assert(layer_id != 0);
|
|
||||||
|
|
||||||
const Layers &layers = data.layers;
|
|
||||||
const LayerParts &prev_layer_parts = layers[layer_id - 1].parts;
|
|
||||||
const LayerParts::const_iterator &prev_part_it = part.prev_parts.front().part_it;
|
|
||||||
size_t index_of_prev_part = prev_part_it - prev_layer_parts.begin();
|
|
||||||
if (prev_part_it->next_parts.size() == 1) {
|
|
||||||
grids.push_back(std::move(prev_grids[index_of_prev_part]));
|
|
||||||
} else { // Need a copy there are multiple parts above previus one
|
|
||||||
grids.push_back(prev_grids[index_of_prev_part]); // copy
|
|
||||||
}
|
|
||||||
// current part grid
|
|
||||||
Grid2D &part_grid = grids.back();
|
|
||||||
|
|
||||||
// merge other grid in case of multiple previous parts
|
|
||||||
for (size_t i = 1; i < part.prev_parts.size(); ++i) {
|
|
||||||
const LayerParts::const_iterator &prev_part_it = part.prev_parts[i].part_it;
|
|
||||||
size_t index_of_prev_part = prev_part_it - prev_layer_parts.begin();
|
|
||||||
if (prev_part_it->next_parts.size() == 1) {
|
|
||||||
part_grid.merge(std::move(prev_grids[index_of_prev_part]));
|
|
||||||
} else { // Need a copy there are multiple parts above previus one
|
|
||||||
Grid2D grid_ = prev_grids[index_of_prev_part]; // copy
|
|
||||||
part_grid.merge(std::move(grid_));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float part_height = data.heights[layer_id];
|
|
||||||
Grid2D::CheckFnc is_supported = [part_height, &config]
|
|
||||||
(const SupportPoint &support_point, const Point &p) -> bool {
|
|
||||||
float diff_height = part_height - support_point.z_height;
|
|
||||||
coord_t r_ = get_supported_radius(support_point, diff_height, config);
|
|
||||||
Point dp = support_point.position_on_layer - p;
|
|
||||||
if (std::abs(dp.x()) > r_) return false;
|
|
||||||
if (std::abs(dp.y()) > r_) return false;
|
|
||||||
double r2 = static_cast<double>(r_);
|
|
||||||
r2 *= r2;
|
|
||||||
return dp.cast<double>().squaredNorm() < r2;
|
|
||||||
};
|
|
||||||
|
|
||||||
// check distance to nearest support points from grid
|
|
||||||
float maximal_radius = scale_(5.f);
|
|
||||||
for (const Point &p : part.samples) {
|
|
||||||
if (!part_grid.exist_true_in_4cell_neighbor(p, is_supported)) {
|
|
||||||
// not supported sample, soo create new support point
|
|
||||||
part_grid.add(SupportPoint{
|
|
||||||
/* head_front_radius */ 0.4,
|
|
||||||
SupportPointType::slope,
|
|
||||||
&part,
|
|
||||||
/* position_on_layer */ p,
|
|
||||||
part_height,
|
|
||||||
/* direction_to_mass */ Point(1,0)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Points uniformly_sample(const ExPolygon &island, const SupportPointGeneratorConfig &cfg) {
|
|
||||||
// TODO: Implement it
|
|
||||||
return Points{island.contour.centroid()};
|
|
||||||
}
|
|
||||||
|
|
||||||
Grid2D support_island(const LayerPart &part, float part_z, const SupportPointGeneratorConfig &cfg) {
|
|
||||||
// Maximal radius of supported area of one support point
|
|
||||||
double max_support_radius = 10.; // cfg.cell_size;
|
|
||||||
|
|
||||||
// maximal radius of support
|
|
||||||
coord_t cell_size = scale_(max_support_radius);
|
|
||||||
|
|
||||||
Grid2D part_grid(cell_size);
|
|
||||||
Points pts = uniformly_sample(*part.shape, cfg);
|
|
||||||
for (const Point &pt : pts)
|
|
||||||
part_grid.add(SupportPoint{
|
|
||||||
/* head_front_radius */ 0.4,
|
|
||||||
SupportPointType::island,
|
|
||||||
&part,
|
|
||||||
/* position_on_layer */ pt,
|
|
||||||
part_z,
|
|
||||||
/* direction_to_mass */ Point(0,0) // from bottom
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
SupportPointGeneratorData Slic3r::sla::prepare_generator_data(
|
|
||||||
std::vector<ExPolygons> &&slices,
|
|
||||||
std::vector<float> &&heights,
|
|
||||||
ThrowOnCancel throw_on_cancel,
|
|
||||||
StatusFunction statusfn
|
|
||||||
) {
|
|
||||||
// check input
|
|
||||||
assert(!slices.empty());
|
|
||||||
assert(slices.size() == heights.size());
|
|
||||||
if (slices.empty() || slices.size() != heights.size())
|
|
||||||
return SupportPointGeneratorData{};
|
|
||||||
|
|
||||||
// Move input into result
|
|
||||||
SupportPointGeneratorData result;
|
|
||||||
result.slices = std::move(slices);
|
|
||||||
result.heights = std::move(heights);
|
|
||||||
|
|
||||||
// Allocate empty layers.
|
|
||||||
result.layers = Layers(result.slices.size(), {});
|
|
||||||
|
|
||||||
// Generate Extents and SampleLayers
|
|
||||||
execution::for_each(ex_tbb, size_t(0), result.slices.size(),
|
|
||||||
[&result, throw_on_cancel](size_t layer_id) {
|
|
||||||
if ((layer_id % 8) == 0)
|
|
||||||
// Don't call the following function too often as it flushes
|
|
||||||
// CPU write caches due to synchronization primitves.
|
|
||||||
throw_on_cancel();
|
|
||||||
|
|
||||||
const double sample_distance_in_mm = scale_(2);
|
|
||||||
const double sample_distance_in_mm2 = sample_distance_in_mm * sample_distance_in_mm;
|
|
||||||
|
|
||||||
Layer &layer = result.layers[layer_id];
|
|
||||||
const ExPolygons &islands = result.slices[layer_id];
|
|
||||||
layer.parts.reserve(islands.size());
|
|
||||||
for (const ExPolygon &island : islands)
|
|
||||||
layer.parts.push_back(LayerPart{
|
|
||||||
&island,
|
|
||||||
get_extents(island.contour),
|
|
||||||
sample(island.contour, sample_distance_in_mm2)
|
|
||||||
});
|
|
||||||
|
|
||||||
}, 32 /*gransize*/);
|
|
||||||
|
|
||||||
// Link parts by intersections
|
|
||||||
execution::for_each(ex_tbb, size_t(1), result.slices.size(),
|
|
||||||
[&result, throw_on_cancel] (size_t layer_id) {
|
|
||||||
if ((layer_id % 2) == 0)
|
|
||||||
// Don't call the following function too often as it flushes CPU write caches due to synchronization primitves.
|
|
||||||
throw_on_cancel();
|
|
||||||
|
|
||||||
LayerParts &parts_above = result.layers[layer_id].parts;
|
|
||||||
LayerParts &parts_below = result.layers[layer_id-1].parts;
|
|
||||||
for (auto it_above = parts_above.begin(); it_above < parts_above.end(); ++it_above) {
|
|
||||||
for (auto it_below = parts_below.begin(); it_below < parts_below.end(); ++it_below) {
|
|
||||||
// Improve: do some sort of parts + skip some of them
|
|
||||||
if (!it_above->shape_extent.overlap(it_below->shape_extent))
|
|
||||||
continue; // no bounding box overlap
|
|
||||||
|
|
||||||
// Improve: test could be done faster way
|
|
||||||
Polygons polys = intersection(*it_above->shape, *it_below->shape);
|
|
||||||
if (polys.empty())
|
|
||||||
continue; // no intersection
|
|
||||||
|
|
||||||
// TODO: check minimal intersection!
|
|
||||||
|
|
||||||
it_above->prev_parts.emplace_back(PartLink{it_below});
|
|
||||||
it_below->next_parts.emplace_back(PartLink{it_above});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 8 /* gransize */);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
SupportPoints Slic3r::sla::generate_support_points(
|
|
||||||
const SupportPointGeneratorData &data,
|
|
||||||
const SupportPointGeneratorConfig &config,
|
|
||||||
ThrowOnCancel throw_on_cancel,
|
|
||||||
StatusFunction statusfn
|
|
||||||
){
|
|
||||||
const Layers &layers = data.layers;
|
|
||||||
double increment = 100.0 / static_cast<double>(layers.size());
|
|
||||||
double status = 0; // current progress
|
|
||||||
int status_int = 0;
|
|
||||||
|
|
||||||
SupportPoints result;
|
|
||||||
std::vector<Grid2D> prev_grids; // same count as previous layer item size
|
|
||||||
for (size_t layer_id = 0; layer_id < layers.size(); ++layer_id) {
|
|
||||||
const Layer &layer = layers[layer_id];
|
|
||||||
|
|
||||||
std::vector<Grid2D> grids;
|
|
||||||
grids.reserve(layer.parts.size());
|
|
||||||
|
|
||||||
for (const LayerPart &part : layer.parts) {
|
|
||||||
if (part.prev_parts.empty()) {
|
|
||||||
// new island - needs support no doubt
|
|
||||||
float part_z = data.heights[layer_id];
|
|
||||||
grids.push_back(support_island(part, part_z, config));
|
|
||||||
} else {
|
|
||||||
sample_part(part, layer_id, data, config, grids, prev_grids);
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect result from grid of top part
|
|
||||||
if (part.next_parts.empty()) {
|
|
||||||
const Grid2D &part_grid = grids.back();
|
|
||||||
SupportPoints sps = part_grid.get_points();
|
|
||||||
result.insert(result.end(),
|
|
||||||
std::make_move_iterator(sps.begin()),
|
|
||||||
std::make_move_iterator(sps.end()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prev_grids = std::move(grids);
|
|
||||||
|
|
||||||
throw_on_cancel();
|
|
||||||
|
|
||||||
int old_status_int = status_int;
|
|
||||||
status += increment;
|
|
||||||
status_int = static_cast<int>(std::round(status));
|
|
||||||
if (old_status_int < status_int)
|
|
||||||
statusfn(status_int);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,190 +0,0 @@
|
|||||||
///|/ Copyright (c) Prusa Research 2024 Filip Sykala @Jony01
|
|
||||||
///|/
|
|
||||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
|
||||||
///|/
|
|
||||||
#ifndef SLA_SUPPORTPOINTGENERATOR_NEW_HPP
|
|
||||||
#define SLA_SUPPORTPOINTGENERATOR_NEW_HPP
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <boost/container/small_vector.hpp>
|
|
||||||
|
|
||||||
#include "libslic3r/Point.hpp"
|
|
||||||
#include "libslic3r/ExPolygon.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r::sla {
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Configuration for automatic support placement
|
|
||||||
/// </summary>
|
|
||||||
struct SupportPointGeneratorConfig{
|
|
||||||
/// <summary>
|
|
||||||
/// 0 mean only one support point for each island
|
|
||||||
/// lower than one mean less amount of support points
|
|
||||||
/// 1 mean fine tuned sampling
|
|
||||||
/// more than one mean bigger amout of support points
|
|
||||||
/// </summary>
|
|
||||||
float density_relative{1.f};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Size range for support point interface (head)
|
|
||||||
/// </summary>
|
|
||||||
MinMax<float> head_diameter = {0.2f, 0.6f}; // [in mm]
|
|
||||||
|
|
||||||
// FIXME: calculate actual pixel area from printer config:
|
|
||||||
// const float pixel_area =
|
|
||||||
// pow(wxGetApp().preset_bundle->project_config.option<ConfigOptionFloat>("display_width") /
|
|
||||||
// wxGetApp().preset_bundle->project_config.option<ConfigOptionInt>("display_pixels_x"), 2.f); //
|
|
||||||
// Minimal island Area to print - TODO: Should be modifiable from UI
|
|
||||||
// !! Filter should be out of sampling algorithm !!
|
|
||||||
float minimal_island_area = pow(0.047f, 2.f); // [in mm^2] pixel_area
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LayerPart; // forward decl.
|
|
||||||
using LayerParts = std::vector<LayerPart>;
|
|
||||||
|
|
||||||
struct PartLink
|
|
||||||
{
|
|
||||||
LayerParts::const_iterator part_it;
|
|
||||||
// float overlap_area; // sum of overlap areas
|
|
||||||
// ExPolygons overlap; // clipper intersection_ex
|
|
||||||
// ExPolygons overhang; // clipper diff_ex
|
|
||||||
};
|
|
||||||
#ifdef NDEBUG
|
|
||||||
// In release mode, use the optimized container.
|
|
||||||
using PartLinks = boost::container::small_vector<PartLink, 4>;
|
|
||||||
#else
|
|
||||||
// In debug mode, use the standard vector, which is well handled by debugger visualizer.
|
|
||||||
using PartLinks = std::vector<PartLink>;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Part on layer is defined by its shape
|
|
||||||
struct LayerPart {
|
|
||||||
// Pointer to expolygon stored in input
|
|
||||||
const ExPolygon *shape;
|
|
||||||
|
|
||||||
// rectangular bounding box of shape
|
|
||||||
BoundingBox shape_extent;
|
|
||||||
|
|
||||||
// uniformly sampled shape contour
|
|
||||||
Slic3r::Points samples;
|
|
||||||
// IMPROVE: sample only overhangs part of shape
|
|
||||||
|
|
||||||
// Parts from previous printed layer, which is connected to current part
|
|
||||||
PartLinks prev_parts;
|
|
||||||
PartLinks next_parts;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// One slice divided into
|
|
||||||
/// </summary>
|
|
||||||
struct Layer
|
|
||||||
{
|
|
||||||
// index into parent Layesr + heights + slices
|
|
||||||
// [[deprecated]] Use index to layers insted of adress from item
|
|
||||||
size_t layer_id;
|
|
||||||
|
|
||||||
// Absolute distance from Zero - copy value from heights<float>
|
|
||||||
// [[deprecated]] Use index to layers insted of adress from item
|
|
||||||
double print_z; // [in mm]
|
|
||||||
|
|
||||||
// data for one expolygon
|
|
||||||
LayerParts parts;
|
|
||||||
};
|
|
||||||
using Layers = std::vector<Layer>;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Keep state of Support Point generation
|
|
||||||
/// Used for resampling with different configuration
|
|
||||||
/// </summary>
|
|
||||||
struct SupportPointGeneratorData
|
|
||||||
{
|
|
||||||
// Input slices of mesh
|
|
||||||
std::vector<ExPolygons> slices;
|
|
||||||
// Same size as slices
|
|
||||||
std::vector<float> heights;
|
|
||||||
|
|
||||||
// link to slices
|
|
||||||
Layers layers;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reason of automatic support placement usage
|
|
||||||
enum class SupportPointType {
|
|
||||||
manual_add,
|
|
||||||
island, // no move
|
|
||||||
slope,
|
|
||||||
thin,
|
|
||||||
stability,
|
|
||||||
edge
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generated support point
|
|
||||||
/// </summary>
|
|
||||||
struct SupportPoint
|
|
||||||
{
|
|
||||||
// radius of the touching interface
|
|
||||||
// Also define force it must keep
|
|
||||||
float head_front_radius{1.f};
|
|
||||||
|
|
||||||
// type
|
|
||||||
SupportPointType type{SupportPointType::manual_add};
|
|
||||||
|
|
||||||
// Pointer on source ExPolygon otherwise nullptr
|
|
||||||
const LayerPart *part{nullptr};
|
|
||||||
|
|
||||||
// 2d coordinate on layer
|
|
||||||
// use only when part is not nullptr
|
|
||||||
Point position_on_layer; // [scaled_ unit]
|
|
||||||
|
|
||||||
// height of part
|
|
||||||
float z_height;
|
|
||||||
|
|
||||||
// 2d direction into expolygon mass
|
|
||||||
// used as ray to positioning point on mesh surface
|
|
||||||
Point direction_to_mass;
|
|
||||||
};
|
|
||||||
using SupportPoints = std::vector<SupportPoint>;
|
|
||||||
|
|
||||||
// call during generation of support points to check cancel event
|
|
||||||
using ThrowOnCancel = std::function<void(void)>;
|
|
||||||
// call to say progress of generation into gui in range from 0 to 100
|
|
||||||
using StatusFunction= std::function<void(int)>;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prepare data for generate support points
|
|
||||||
/// Used for interactive resampling to store permanent data between configuration changes.,
|
|
||||||
/// Everything which could be prepared are stored into result.
|
|
||||||
/// Need to regenerate on mesh change(Should be connected with ObjectId) OR change of slicing heights
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="slices">Countour cut from mesh</param>
|
|
||||||
/// <param name="heights">Heights of the slices - Same size as slices</param>
|
|
||||||
/// <param name="throw_on_cancel">Call in meanwhile to check cancel event</param>
|
|
||||||
/// <param name="statusfn">Say progress of generation into gui</param>
|
|
||||||
/// <returns>Data prepared for generate support points</returns>
|
|
||||||
SupportPointGeneratorData prepare_generator_data(
|
|
||||||
std::vector<ExPolygons> &&slices,
|
|
||||||
std::vector<float> &&heights,
|
|
||||||
ThrowOnCancel throw_on_cancel,
|
|
||||||
StatusFunction statusfn
|
|
||||||
);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generate support points on islands by configuration parameters
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="data">Preprocessed data needed for sampling</param>
|
|
||||||
/// <param name="config">Define density of samples</param>
|
|
||||||
/// <param name="throw_on_cancel">Call in meanwhile to check cancel event</param>
|
|
||||||
/// <param name="statusfn">Progress of generation into gui</param>
|
|
||||||
/// <returns>Generated support points</returns>
|
|
||||||
SupportPoints generate_support_points(
|
|
||||||
const SupportPointGeneratorData &data,
|
|
||||||
const SupportPointGeneratorConfig &config,
|
|
||||||
ThrowOnCancel throw_on_cancel,
|
|
||||||
StatusFunction statusfn
|
|
||||||
);
|
|
||||||
|
|
||||||
} // namespace Slic3r::sla
|
|
||||||
|
|
||||||
#endif // SLA_SUPPORTPOINTGENERATOR_NEW_HPP
|
|
@ -1255,8 +1255,12 @@ SLAPrintObject::get_parts_to_slice(SLAPrintObjectStep untilstep) const
|
|||||||
sla::SupportPoints SLAPrintObject::transformed_support_points() const
|
sla::SupportPoints SLAPrintObject::transformed_support_points() const
|
||||||
{
|
{
|
||||||
assert(model_object());
|
assert(model_object());
|
||||||
|
auto spts = model_object()->sla_support_points;
|
||||||
return sla::transformed_support_points(*model_object(), trafo());
|
Transform3f tr = trafo().cast<float>();
|
||||||
|
for (sla::SupportPoint &suppt : spts) {
|
||||||
|
suppt.pos = tr * suppt.pos;
|
||||||
|
}
|
||||||
|
return spts;
|
||||||
}
|
}
|
||||||
|
|
||||||
sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const
|
sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const
|
||||||
|
@ -51,7 +51,6 @@
|
|||||||
#include "libslic3r/SLA/Hollowing.hpp"
|
#include "libslic3r/SLA/Hollowing.hpp"
|
||||||
#include "libslic3r/SLA/JobController.hpp"
|
#include "libslic3r/SLA/JobController.hpp"
|
||||||
#include "libslic3r/SLA/RasterBase.hpp"
|
#include "libslic3r/SLA/RasterBase.hpp"
|
||||||
#include "libslic3r/SLA/SupportPoint.hpp"
|
|
||||||
#include "libslic3r/SLA/SupportTree.hpp"
|
#include "libslic3r/SLA/SupportTree.hpp"
|
||||||
#include "libslic3r/SLA/SupportTreeStrategies.hpp"
|
#include "libslic3r/SLA/SupportTreeStrategies.hpp"
|
||||||
#include "libslic3r/SLAPrint.hpp"
|
#include "libslic3r/SLAPrint.hpp"
|
||||||
@ -632,24 +631,20 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
|
|||||||
// we will do the autoplacement. Otherwise we will just blindly copy the
|
// we will do the autoplacement. Otherwise we will just blindly copy the
|
||||||
// frontend data into the backend cache.
|
// frontend data into the backend cache.
|
||||||
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
|
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
|
||||||
|
|
||||||
// calculate heights of slices (slices are calculated already)
|
|
||||||
const std::vector<float>& heights = po.m_model_height_levels;
|
|
||||||
|
|
||||||
throw_if_canceled();
|
throw_if_canceled();
|
||||||
sla::SupportPointGenerator::Config config;
|
sla::SupportPointGeneratorConfig config;
|
||||||
const SLAPrintObjectConfig& cfg = po.config();
|
const SLAPrintObjectConfig& cfg = po.config();
|
||||||
|
|
||||||
// the density config value is in percents:
|
// the density config value is in percents:
|
||||||
config.density_relative = float(cfg.support_points_density_relative / 100.f);
|
config.density_relative = float(cfg.support_points_density_relative / 100.f);
|
||||||
config.minimal_distance = float(cfg.support_points_minimal_distance);
|
|
||||||
switch (cfg.support_tree_type) {
|
switch (cfg.support_tree_type) {
|
||||||
case sla::SupportTreeType::Default:
|
case sla::SupportTreeType::Default:
|
||||||
case sla::SupportTreeType::Organic:
|
case sla::SupportTreeType::Organic:
|
||||||
config.head_diameter = float(cfg.support_head_front_diameter);
|
config.head_diameter = {float(cfg.support_head_front_diameter), .0};
|
||||||
break;
|
break;
|
||||||
case sla::SupportTreeType::Branching:
|
case sla::SupportTreeType::Branching:
|
||||||
config.head_diameter = float(cfg.branchingsupport_head_front_diameter);
|
config.head_diameter = {float(cfg.branchingsupport_head_front_diameter), .0};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,12 +661,29 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
|
|||||||
|
|
||||||
// Construction of this object does the calculation.
|
// Construction of this object does the calculation.
|
||||||
throw_if_canceled();
|
throw_if_canceled();
|
||||||
sla::SupportPointGenerator auto_supports(
|
|
||||||
po.m_supportdata->input.emesh, po.get_model_slices(),
|
|
||||||
heights, config, [this]() { throw_if_canceled(); }, statuscb);
|
|
||||||
|
|
||||||
// Now let's extract the result.
|
// TODO: filter small unprintable islands in slices
|
||||||
std::vector<sla::SupportPoint>& points = auto_supports.output();
|
// (Island with area smaller than 1 pixel was skipped in support generator)
|
||||||
|
|
||||||
|
std::vector<ExPolygons> slices = po.get_model_slices(); // copy
|
||||||
|
std::vector<float> heights = po.m_model_height_levels; // copy
|
||||||
|
sla::ThrowOnCancel cancel = [this]() { throw_if_canceled(); };
|
||||||
|
sla::StatusFunction status = statuscb;
|
||||||
|
sla::SupportPointGeneratorData data =
|
||||||
|
sla::prepare_generator_data(std::move(slices), std::move(heights), cancel, status);
|
||||||
|
|
||||||
|
sla::LayerSupportPoints layer_support_points =
|
||||||
|
sla::generate_support_points(data, config, cancel, status);
|
||||||
|
|
||||||
|
const AABBMesh& emesh = po.m_supportdata->input.emesh;
|
||||||
|
// Maximal move of support point to mesh surface,
|
||||||
|
// no more than height of layer
|
||||||
|
assert(po.m_model_height_levels.size() > 1);
|
||||||
|
double allowed_move = (po.m_model_height_levels[1] - po.m_model_height_levels[0]) +
|
||||||
|
std::numeric_limits<float>::epsilon();
|
||||||
|
sla::SupportPoints support_points =
|
||||||
|
sla::move_on_mesh_surface(layer_support_points, emesh, allowed_move, cancel);
|
||||||
|
|
||||||
throw_if_canceled();
|
throw_if_canceled();
|
||||||
|
|
||||||
MeshSlicingParamsEx params;
|
MeshSlicingParamsEx params;
|
||||||
@ -691,9 +703,9 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
|
|||||||
});
|
});
|
||||||
|
|
||||||
SuppPtMask mask{blockers, enforcers, po.config().support_enforcers_only.getBool()};
|
SuppPtMask mask{blockers, enforcers, po.config().support_enforcers_only.getBool()};
|
||||||
filter_support_points_by_modifiers(points, mask, po.m_model_height_levels);
|
filter_support_points_by_modifiers(support_points, mask, po.m_model_height_levels);
|
||||||
|
|
||||||
po.m_supportdata->input.pts = points;
|
po.m_supportdata->input.pts = support_points;
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug)
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
<< "Automatic support points: "
|
<< "Automatic support points: "
|
||||||
@ -717,10 +729,17 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po)
|
|||||||
// If the zero elevation mode is engaged, we have to filter out all the
|
// If the zero elevation mode is engaged, we have to filter out all the
|
||||||
// points that are on the bottom of the object
|
// points that are on the bottom of the object
|
||||||
if (is_zero_elevation(po.config())) {
|
if (is_zero_elevation(po.config())) {
|
||||||
remove_bottom_points(po.m_supportdata->input.pts,
|
// remove_bottom_points
|
||||||
float(
|
std::vector<sla::SupportPoint> &pts = po.m_supportdata->input.pts;
|
||||||
po.m_supportdata->input.zoffset +
|
float lvl(po.m_supportdata->input.zoffset + EPSILON);
|
||||||
EPSILON));
|
|
||||||
|
// get iterator to the reorganized vector end
|
||||||
|
auto endit = std::remove_if(pts.begin(), pts.end(),
|
||||||
|
[lvl](const sla::SupportPoint &sp) {
|
||||||
|
return sp.pos.z() <= lvl; });
|
||||||
|
|
||||||
|
// erase all elements after the new end
|
||||||
|
pts.erase(endit, pts.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
po.m_supportdata->input.cfg = make_support_cfg(po.m_config);
|
po.m_supportdata->input.cfg = make_support_cfg(po.m_config);
|
||||||
|
@ -212,7 +212,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection)
|
|||||||
if (size_t(m_hover_id) == i && m_editing_mode) // ignore hover state unless editing mode is active
|
if (size_t(m_hover_id) == i && m_editing_mode) // ignore hover state unless editing mode is active
|
||||||
render_color = { 0.f, 1.f, 1.f, 1.f };
|
render_color = { 0.f, 1.f, 1.f, 1.f };
|
||||||
else { // neigher hover nor picking
|
else { // neigher hover nor picking
|
||||||
bool supports_new_island = m_lock_unique_islands && support_point.is_new_island;
|
bool supports_new_island = m_lock_unique_islands && support_point.type == sla::SupportPointType::island;
|
||||||
if (m_editing_mode) {
|
if (m_editing_mode) {
|
||||||
if (point_selected)
|
if (point_selected)
|
||||||
render_color = { 1.f, 0.3f, 0.3f, 1.f};
|
render_color = { 1.f, 0.3f, 0.3f, 1.f};
|
||||||
@ -324,7 +324,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||||||
std::pair<Vec3f, Vec3f> pos_and_normal;
|
std::pair<Vec3f, Vec3f> pos_and_normal;
|
||||||
if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection
|
if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection
|
||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add support point"));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Add support point"));
|
||||||
m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second);
|
m_editing_cache.emplace_back(sla::SupportPoint{pos_and_normal.first, m_new_point_head_diameter/2.f}, false, pos_and_normal.second);
|
||||||
m_parent.set_as_dirty();
|
m_parent.set_as_dirty();
|
||||||
m_wait_for_up_event = true;
|
m_wait_for_up_event = true;
|
||||||
unregister_point_raycasters_for_picking();
|
unregister_point_raycasters_for_picking();
|
||||||
@ -479,7 +479,7 @@ void GLGizmoSlaSupports::delete_selected_points(bool force)
|
|||||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete support point"));
|
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Delete support point"));
|
||||||
|
|
||||||
for (unsigned int idx=0; idx<m_editing_cache.size(); ++idx) {
|
for (unsigned int idx=0; idx<m_editing_cache.size(); ++idx) {
|
||||||
if (m_editing_cache[idx].selected && (!m_editing_cache[idx].support_point.is_new_island || !m_lock_unique_islands || force)) {
|
if (m_editing_cache[idx].selected && (!m_editing_cache[idx].support_point.is_island() || !m_lock_unique_islands || force)) {
|
||||||
m_editing_cache.erase(m_editing_cache.begin() + (idx--));
|
m_editing_cache.erase(m_editing_cache.begin() + (idx--));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -914,7 +914,7 @@ void GLGizmoSlaSupports::on_dragging(const UpdateData &data)
|
|||||||
{
|
{
|
||||||
assert(m_hover_id != -1);
|
assert(m_hover_id != -1);
|
||||||
if (!m_editing_mode) return;
|
if (!m_editing_mode) return;
|
||||||
if (m_editing_cache[m_hover_id].support_point.is_new_island && m_lock_unique_islands)
|
if (m_editing_cache[m_hover_id].support_point.is_island() && m_lock_unique_islands)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::pair<Vec3f, Vec3f> pos_and_normal;
|
std::pair<Vec3f, Vec3f> pos_and_normal;
|
||||||
@ -922,7 +922,7 @@ void GLGizmoSlaSupports::on_dragging(const UpdateData &data)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_editing_cache[m_hover_id].support_point.pos = pos_and_normal.first;
|
m_editing_cache[m_hover_id].support_point.pos = pos_and_normal.first;
|
||||||
m_editing_cache[m_hover_id].support_point.is_new_island = false;
|
m_editing_cache[m_hover_id].support_point.type = sla::SupportPointType::manual_add;
|
||||||
m_editing_cache[m_hover_id].normal = pos_and_normal.second;
|
m_editing_cache[m_hover_id].normal = pos_and_normal.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1124,7 +1124,7 @@ void GLGizmoSlaSupports::get_data_from_backend()
|
|||||||
const std::vector<sla::SupportPoint>& points = po->get_support_points();
|
const std::vector<sla::SupportPoint>& points = po->get_support_points();
|
||||||
auto mat = po->trafo().inverse().cast<float>();
|
auto mat = po->trafo().inverse().cast<float>();
|
||||||
for (unsigned int i=0; i<points.size();++i)
|
for (unsigned int i=0; i<points.size();++i)
|
||||||
m_normal_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island));
|
m_normal_cache.emplace_back(sla::SupportPoint{mat * points[i].pos, points[i].head_front_radius});
|
||||||
|
|
||||||
mo->sla_points_status = sla::PointsStatus::AutoGenerated;
|
mo->sla_points_status = sla::PointsStatus::AutoGenerated;
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user