Merge branch 'lm_fdm_custom_supports_backend'

This commit is contained in:
Lukas Matena 2020-04-24 15:16:33 +02:00
commit 8afc9338de
13 changed files with 358 additions and 46 deletions

View File

@ -1040,6 +1040,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
for (ModelVolume *volume : volumes) {
const auto volume_matrix = volume->get_matrix();
volume->m_supported_facets.clear();
if (! volume->is_model_part()) {
// Modifiers are not cut, but we still need to add the instance transformation
// to the modifier volume transformation to preserve their shape properly.
@ -1739,6 +1741,41 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
return ret;
}
std::vector<int> FacetsAnnotation::get_facets(FacetSupportType type) const
{
std::vector<int> out;
for (auto& [facet_idx, this_type] : m_data)
if (this_type == type)
out.push_back(facet_idx);
return out;
}
void FacetsAnnotation::set_facet(int idx, FacetSupportType type)
{
bool changed = true;
if (type == FacetSupportType::NONE)
changed = m_data.erase(idx) != 0;
else
m_data[idx] = type;
if (changed)
update_timestamp();
}
void FacetsAnnotation::clear()
{
m_data.clear();
update_timestamp();
}
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
bool model_object_list_equal(const Model &model_old, const Model &model_new)
@ -1802,6 +1839,16 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO
return false;
}
bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new) {
assert(! model_volume_list_changed(mo, mo_new, ModelVolumeType::MODEL_PART));
assert(mo.volumes.size() == mo_new.volumes.size());
for (size_t i=0; i<mo.volumes.size(); ++i) {
if (! mo_new.volumes[i]->m_supported_facets.is_same_as(mo.volumes[i]->m_supported_facets))
return true;
}
return false;
};
extern bool model_has_multi_part_objects(const Model &model)
{
for (const ModelObject *model_object : model.objects)

View File

@ -19,6 +19,7 @@
#include <string>
#include <utility>
#include <vector>
#include <chrono>
namespace cereal {
class BinaryInputArchive;
@ -214,8 +215,8 @@ public:
when user expects that. */
Vec3d origin_translation;
Model* get_model() { return m_model; };
const Model* get_model() const { return m_model; };
Model* get_model() { return m_model; }
const Model* get_model() const { return m_model; }
ModelVolume* add_volume(const TriangleMesh &mesh);
ModelVolume* add_volume(TriangleMesh &&mesh);
@ -391,6 +392,34 @@ enum class ModelVolumeType : int {
SUPPORT_BLOCKER,
};
enum class FacetSupportType : int8_t {
NONE = 0,
ENFORCER = 1,
BLOCKER = 2
};
class FacetsAnnotation {
public:
using ClockType = std::chrono::steady_clock;
std::vector<int> get_facets(FacetSupportType type) const;
void set_facet(int idx, FacetSupportType type);
void clear();
ClockType::time_point get_timestamp() const { return timestamp; }
bool is_same_as(const FacetsAnnotation& other) const {
return timestamp == other.get_timestamp();
}
private:
std::map<int, FacetSupportType> m_data;
ClockType::time_point timestamp;
void update_timestamp() {
timestamp = ClockType::now();
}
};
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
// ModelVolume instances are owned by a ModelObject.
class ModelVolume final : public ObjectBase
@ -421,8 +450,11 @@ public:
// overriding the global Slic3r settings and the ModelObject settings.
ModelConfig config;
// List of mesh facets to be supported/unsupported.
FacetsAnnotation m_supported_facets;
// A parent object owning this modifier volume.
ModelObject* get_object() const { return this->object; };
ModelObject* get_object() const { return this->object; }
ModelVolumeType type() const { return m_type; }
void set_type(const ModelVolumeType t) { m_type = t; }
bool is_model_part() const { return m_type == ModelVolumeType::MODEL_PART; }
@ -548,7 +580,9 @@ private:
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) :
ObjectBase(other),
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull),
config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation),
m_supported_facets(other.m_supported_facets)
{
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == other.id() && this->config.id() == other.config.id());
@ -565,6 +599,8 @@ private:
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id());
m_supported_facets.clear();
}
ModelVolume& operator=(ModelVolume &rhs) = delete;
@ -820,8 +856,7 @@ public:
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
private:
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); };
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }
void assign_new_unique_ids_recursive();
void update_links_bottom_up_recursive();
@ -848,6 +883,10 @@ extern bool model_object_list_extended(const Model &model_old, const Model &mode
// than the old ModelObject.
extern bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolumeType type);
// Test whether the now ModelObject has newer custom supports data than the old one.
// The function assumes that volumes list is synchronized.
extern bool model_custom_supports_data_changed(const ModelObject& mo, const ModelObject& mo_new);
// If the model has multi-part objects, then it is currently not supported by the SLA mode.
// Either the model cannot be loaded, or a SLA printer has to be activated.
extern bool model_has_multi_part_objects(const Model &model);

View File

@ -114,6 +114,7 @@ public:
Point& operator+=(const Point& rhs) { (*this)(0) += rhs(0); (*this)(1) += rhs(1); return *this; }
Point& operator-=(const Point& rhs) { (*this)(0) -= rhs(0); (*this)(1) -= rhs(1); return *this; }
Point& operator*=(const double &rhs) { (*this)(0) = coord_t((*this)(0) * rhs); (*this)(1) = coord_t((*this)(1) * rhs); return *this; }
Point operator*(const double &rhs) { return Point((*this)(0) * rhs, (*this)(1) * rhs); }
void rotate(double angle);
void rotate(double angle, const Point &center);

View File

@ -404,6 +404,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
// Copy the ModelVolume data.
mv_dst.name = mv_src.name;
static_cast<DynamicPrintConfig&>(mv_dst.config) = static_cast<const DynamicPrintConfig&>(mv_src.config);
mv_dst.m_supported_facets = mv_src.m_supported_facets;
//FIXME what to do with the materials?
// mv_dst.m_material_id = mv_src.m_material_id;
++ i_src;
@ -854,7 +855,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
// Copy content of the ModelObject including its ID, do not change the parent.
model_object.assign_copy(model_object_new);
} else if (support_blockers_differ || support_enforcers_differ) {
} else if (support_blockers_differ || support_enforcers_differ || model_custom_supports_data_changed(model_object, model_object_new)) {
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
this->call_cancel_callback();
update_apply_status(false);
@ -862,8 +863,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it)
update_apply_status(it->print_object->invalidate_step(posSupportMaterial));
// Copy just the support volumes.
model_volume_list_update_supports(model_object, model_object_new);
if (support_enforcers_differ || support_blockers_differ) {
// Copy just the support volumes.
model_volume_list_update_supports(model_object, model_object_new);
}
}
if (! model_parts_differ && ! modifiers_differ) {
// Synchronize Object's config.
@ -881,7 +884,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
}
}
// Synchronize (just copy) the remaining data of ModelVolumes (name, config).
// Synchronize (just copy) the remaining data of ModelVolumes (name, config, custom supports data).
//FIXME What to do with m_material_id?
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);

View File

@ -192,6 +192,11 @@ public:
std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
// Helpers to project custom supports on slices
void project_and_append_custom_supports(FacetSupportType type, std::vector<ExPolygons>& expolys) const;
void project_and_append_custom_enforcers(std::vector<ExPolygons>& enforcers) const { project_and_append_custom_supports(FacetSupportType::ENFORCER, enforcers); }
void project_and_append_custom_blockers(std::vector<ExPolygons>& blockers) const { project_and_append_custom_supports(FacetSupportType::BLOCKER, blockers); }
private:
// to be called from Print only.
friend class Print;

View File

@ -2645,4 +2645,169 @@ void PrintObject::_generate_support_material()
support_material.generate(*this);
}
void PrintObject::project_and_append_custom_supports(
FacetSupportType type, std::vector<ExPolygons>& expolys) const
{
for (const ModelVolume* mv : this->model_object()->volumes) {
const std::vector<int> custom_facets = mv->m_supported_facets.get_facets(type);
if (custom_facets.empty())
continue;
const TriangleMesh& mesh = mv->mesh();
const Transform3f& tr1 = mv->get_matrix().cast<float>();
const Transform3f& tr2 = this->trafo().cast<float>();
const Transform3f tr = tr2 * tr1;
// The projection will be at most a pentagon. Let's minimize heap
// reallocations by saving in in the following struct.
// Points are used so that scaling can be done in parallel
// and they can be moved from to create an ExPolygon later.
struct LightPolygon {
LightPolygon() { pts.reserve(5); }
Points pts;
void add(const Vec2f& pt) {
pts.emplace_back(scale_(pt.x()), scale_(pt.y()));
assert(pts.size <= 5);
}
};
// Structure to collect projected polygons. One element for each triangle.
// Saves vector of polygons and layer_id of the first one.
struct TriangleProjections {
size_t first_layer_id;
std::vector<LightPolygon> polygons;
};
// Vector to collect resulting projections from each triangle.
std::vector<TriangleProjections> projections_of_triangles(custom_facets.size());
// Iterate over all triangles.
tbb::parallel_for(
tbb::blocked_range<size_t>(0, custom_facets.size() - 1),
[&](const tbb::blocked_range<size_t>& range) {
for (size_t idx = range.begin(); idx < range.end(); ++ idx) {
std::array<Vec3f, 3> facet;
// Transform the triangle into worlds coords.
for (int i=0; i<3; ++i)
facet[i] = tr * mesh.its.vertices[mesh.its.indices[custom_facets[idx]](i)];
// Ignore triangles with upward-pointing normal.
if ((facet[1]-facet[0]).cross(facet[2]-facet[0]).z() > 0.)
continue;
// Sort the three vertices according to z-coordinate.
std::sort(facet.begin(), facet.end(),
[](const Vec3f& pt1, const Vec3f&pt2) {
return pt1.z() < pt2.z();
});
std::array<Vec2f, 3> trianglef;
for (int i=0; i<3; ++i) {
trianglef[i] = Vec2f(facet[i].x(), facet[i].y());
trianglef[i] += Vec2f(unscale<float>(this->center_offset().x()),
unscale<float>(this->center_offset().y()));
}
// Find lowest slice not below the triangle.
auto it = std::lower_bound(layers().begin(), layers().end(), facet[0].z()+EPSILON,
[](const Layer* l1, float z) {
return l1->slice_z < z;
});
// Count how many projections will be generated for this triangle
// and allocate respective amount in projections_of_triangles.
projections_of_triangles[idx].first_layer_id = it-layers().begin();
size_t last_layer_id = projections_of_triangles[idx].first_layer_id;
// The cast in the condition below is important. The comparison must
// be an exact opposite of the one lower in the code where
// the polygons are appended. And that one is on floats.
while (last_layer_id + 1 < layers().size()
&& float(layers()[last_layer_id]->slice_z) <= facet[2].z())
++last_layer_id;
projections_of_triangles[idx].polygons.resize(
last_layer_id - projections_of_triangles[idx].first_layer_id + 1);
// Calculate how to move points on triangle sides per unit z increment.
Vec2f ta(trianglef[1] - trianglef[0]);
Vec2f tb(trianglef[2] - trianglef[0]);
ta *= 1./(facet[1].z() - facet[0].z());
tb *= 1./(facet[2].z() - facet[0].z());
// Projection on current slice will be build directly in place.
LightPolygon* proj = &projections_of_triangles[idx].polygons[0];
proj->add(trianglef[0]);
bool passed_first = false;
bool stop = false;
// Project a sub-polygon on all slices intersecting the triangle.
while (it != layers().end()) {
const float z = (*it)->slice_z;
// Projections of triangle sides intersections with slices.
// a moves along one side, b tracks the other.
Vec2f a;
Vec2f b;
// If the middle vertex was already passed, append the vertex
// and use ta for tracking the remaining side.
if (z > facet[1].z() && ! passed_first) {
proj->add(trianglef[1]);
ta = trianglef[2]-trianglef[1];
ta *= 1./(facet[2].z() - facet[1].z());
passed_first = true;
}
// This slice is above the triangle already.
if (z > facet[2].z() || it+1 == layers().end()) {
proj->add(trianglef[2]);
stop = true;
}
else {
// Move a, b along the side it currently tracks to get
// projected intersection with current slice.
a = passed_first ? (trianglef[1]+ta*(z-facet[1].z()))
: (trianglef[0]+ta*(z-facet[0].z()));
b = trianglef[0]+tb*(z-facet[0].z());
proj->add(a);
proj->add(b);
}
if (stop)
break;
// Advance to the next layer.
++it;
++proj;
assert(proj <= &projections_of_triangles[idx].polygons.back() );
// a, b are first two points of the polygon for the next layer.
proj->add(b);
proj->add(a);
}
}
}); // end of parallel_for
// Make sure that the output vector can be used.
expolys.resize(layers().size());
// Now append the collected polygons to respective layers.
for (auto& trg : projections_of_triangles) {
int layer_id = trg.first_layer_id;
if (layer_id == 0)
continue;
for (const LightPolygon& poly : trg.polygons) {
expolys[layer_id-1].emplace_back(std::move(poly.pts));
++layer_id;
}
}
} // loop over ModelVolumes
}
} // namespace Slic3r

View File

@ -971,6 +971,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
std::vector<ExPolygons> enforcers = object.slice_support_enforcers();
std::vector<ExPolygons> blockers = object.slice_support_blockers();
// Append custom supports.
object.project_and_append_custom_enforcers(enforcers);
object.project_and_append_custom_blockers(blockers);
// Output layers, sorted by top Z.
MyLayersPtr contact_out;

View File

@ -259,7 +259,8 @@ Point Bed3D::point_projection(const Point& point) const
return m_polygon.point_projection(point);
}
void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) const
void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture) const
{
m_scale_factor = scale_factor;
@ -270,9 +271,9 @@ void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool sho
switch (m_type)
{
case System: { render_system(canvas, bottom); break; }
case System: { render_system(canvas, bottom, show_texture); break; }
default:
case Custom: { render_custom(canvas, bottom); break; }
case Custom: { render_custom(canvas, bottom, show_texture); break; }
}
glsafe(::glDisable(GL_DEPTH_TEST));
@ -384,12 +385,13 @@ void Bed3D::render_axes() const
m_axes.render();
}
void Bed3D::render_system(GLCanvas3D& canvas, bool bottom) const
void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) const
{
if (!bottom)
render_model();
render_texture(bottom, canvas);
if (show_texture)
render_texture(bottom, canvas);
}
void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
@ -564,7 +566,7 @@ void Bed3D::render_model() const
}
}
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) const
{
if (m_texture_filename.empty() && m_model_filename.empty())
{
@ -575,7 +577,8 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const
if (!bottom)
render_model();
render_texture(bottom, canvas);
if (show_texture)
render_texture(bottom, canvas);
}
void Bed3D::render_default(bool bottom) const

View File

@ -103,11 +103,15 @@ public:
// Return true if the bed shape changed, so the calee will update the UI.
bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model);
const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; }
const BoundingBoxf3& get_bounding_box(bool extended) const {
return extended ? m_extended_bounding_box : m_bounding_box;
}
bool contains(const Point& point) const;
Point point_projection(const Point& point) const;
void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) const;
void render(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture) const;
private:
void calc_bounding_boxes() const;
@ -115,10 +119,10 @@ private:
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
std::tuple<EType, std::string, std::string> detect_type(const Pointfs& shape) const;
void render_axes() const;
void render_system(GLCanvas3D& canvas, bool bottom) const;
void render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) const;
void render_texture(bool bottom, GLCanvas3D& canvas) const;
void render_model() const;
void render_custom(GLCanvas3D& canvas, bool bottom) const;
void render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) const;
void render_default(bool bottom) const;
void reset();
};

View File

@ -5341,14 +5341,19 @@ void GLCanvas3D::_render_background() const
glsafe(::glPopMatrix());
}
void GLCanvas3D::_render_bed(float theta, bool show_axes) const
void GLCanvas3D::_render_bed(bool bottom, bool show_axes) const
{
float scale_factor = 1.0;
#if ENABLE_RETINA_GL
scale_factor = m_retina_helper->get_scale_factor();
#endif // ENABLE_RETINA_GL
bool show_texture = ! bottom ||
(m_gizmos.get_current_type() != GLGizmosManager::FdmSupports
&& m_gizmos.get_current_type() != GLGizmosManager::SlaSupports);
#if ENABLE_NON_STATIC_CANVAS_MANAGER
wxGetApp().plater()->get_bed().render(const_cast<GLCanvas3D&>(*this), theta, scale_factor, show_axes);
wxGetApp().plater()->get_bed().render(const_cast<GLCanvas3D&>(*this), bottom, scale_factor, show_axes, show_texture);
#else
m_bed.render(const_cast<GLCanvas3D&>(*this), theta, scale_factor, show_axes);
#endif // ENABLE_NON_STATIC_CANVAS_MANAGER

View File

@ -755,7 +755,7 @@ private:
void _picking_pass() const;
void _rectangular_selection_picking_pass() const;
void _render_background() const;
void _render_bed(float theta, bool show_axes) const;
void _render_bed(bool bottom, bool show_axes) const;
void _render_objects() const;
void _render_selection() const;
#if ENABLE_RENDER_SELECTION_CENTER

View File

@ -8,6 +8,7 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/PresetBundle.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "libslic3r/Model.hpp"
@ -45,6 +46,7 @@ bool GLGizmoFdmSupports::on_init()
m_desc["block"] = _L("Block supports");
m_desc["remove_caption"] = _L("Shift + Left mouse button") + ": ";
m_desc["remove"] = _L("Remove selection");
m_desc["remove_all"] = _L("Remove all");
return true;
}
@ -191,7 +193,16 @@ void GLGizmoFdmSupports::update_mesh()
// This mesh does not account for the possible Z up SLA offset.
const TriangleMesh* mesh = &mv->mesh();
m_selected_facets[volume_id].assign(mesh->its.indices.size(), SelType::NONE);
m_selected_facets[volume_id].assign(mesh->its.indices.size(), FacetSupportType::NONE);
// Load current state from ModelVolume.
for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) {
const std::vector<int>& list = mv->m_supported_facets.get_facets(type);
for (int i : list)
m_selected_facets[volume_id][i] = type;
}
update_vertex_buffers(mv, volume_id, true, true);
m_neighbors[volume_id].resize(3 * mesh->its.indices.size());
// Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets
@ -247,16 +258,16 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|| action == SLAGizmoEventType::RightDown
|| (action == SLAGizmoEventType::Dragging && m_button_down != Button::None)) {
SelType new_state = SelType::NONE;
FacetSupportType new_state = FacetSupportType::NONE;
if (! shift_down) {
if (action == SLAGizmoEventType::Dragging)
new_state = m_button_down == Button::Left
? SelType::ENFORCER
: SelType::BLOCKER;
? FacetSupportType::ENFORCER
: FacetSupportType::BLOCKER;
else
new_state = action == SLAGizmoEventType::LeftDown
? SelType::ENFORCER
: SelType::BLOCKER;
? FacetSupportType::ENFORCER
: FacetSupportType::BLOCKER;
}
const Camera& camera = wxGetApp().plater()->get_camera();
@ -383,9 +394,9 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
// Now just select all facets that passed.
for (size_t next_facet : facets_to_select) {
SelType& facet = m_selected_facets[mesh_id][next_facet];
FacetSupportType& facet = m_selected_facets[mesh_id][next_facet];
if (facet != new_state && facet != SelType::NONE) {
if (facet != new_state && facet != FacetSupportType::NONE) {
// this triangle is currently in the other VBA.
// Both VBAs need to be refreshed.
update_both = true;
@ -395,8 +406,8 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
}
update_vertex_buffers(mv, mesh_id,
new_state == SelType::ENFORCER || update_both,
new_state == SelType::BLOCKER || update_both
new_state == FacetSupportType::ENFORCER || update_both,
new_state == FacetSupportType::BLOCKER || update_both
);
}
@ -420,6 +431,18 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp)
&& m_button_down != Button::None) {
m_button_down = Button::None;
// Synchronize gizmo with ModelVolume data.
ModelObject* mo = m_c->selection_info()->model_object();
int idx = -1;
for (ModelVolume* mv : mo->volumes) {
++idx;
if (! mv->is_model_part())
continue;
for (int i=0; i<int(m_selected_facets[idx].size()); ++i)
mv->m_supported_facets.set_facet(i, m_selected_facets[idx][i]);
}
return true;
}
@ -434,16 +457,16 @@ void GLGizmoFdmSupports::update_vertex_buffers(const ModelVolume* mv,
{
const TriangleMesh* mesh = &mv->mesh();
for (SelType type : {SelType::ENFORCER, SelType::BLOCKER}) {
if ((type == SelType::ENFORCER && ! update_enforcers)
|| (type == SelType::BLOCKER && ! update_blockers))
for (FacetSupportType type : {FacetSupportType::ENFORCER, FacetSupportType::BLOCKER}) {
if ((type == FacetSupportType::ENFORCER && ! update_enforcers)
|| (type == FacetSupportType::BLOCKER && ! update_blockers))
continue;
GLIndexedVertexArray& iva = m_ivas[mesh_id][type==SelType::ENFORCER ? 0 : 1];
GLIndexedVertexArray& iva = m_ivas[mesh_id][type==FacetSupportType::ENFORCER ? 0 : 1];
iva.release_geometry();
size_t triangle_cnt=0;
for (size_t facet_idx=0; facet_idx<m_selected_facets[mesh_id].size(); ++facet_idx) {
SelType status = m_selected_facets[mesh_id][facet_idx];
FacetSupportType status = m_selected_facets[mesh_id][facet_idx];
if (status != type)
continue;
for (int i=0; i<3; ++i)
@ -471,6 +494,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
const float minimal_slider_width = m_imgui->scaled(4.f);
float caption_max = 0.f;
@ -484,6 +508,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
float window_width = minimal_slider_width + std::max(cursor_slider_left, clipping_slider_left);
window_width = std::max(window_width, total_text_max);
window_width = std::max(window_width, button_width);
auto draw_text_with_caption = [this, &caption_max](const wxString& caption, const wxString& text) {
static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
@ -499,6 +524,20 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
m_imgui->text("");
if (m_imgui->button(m_desc.at("remove_all"))) {
ModelObject* mo = m_c->selection_info()->model_object();
int idx = -1;
for (ModelVolume* mv : mo->volumes) {
++idx;
if (mv->is_model_part()) {
m_selected_facets[idx].assign(m_selected_facets[idx].size(), FacetSupportType::NONE);
mv->m_supported_facets.clear();
update_vertex_buffers(mv, idx, true, true);
m_parent.set_as_dirty();
}
}
}
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
m_imgui->text(m_desc.at("cursor_size"));

View File

@ -9,6 +9,9 @@
namespace Slic3r {
enum class FacetSupportType : int8_t;
namespace GUI {
enum class SLAGizmoEventType : unsigned char;
@ -26,15 +29,9 @@ private:
static constexpr float CursorRadiusMax = 8.f;
static constexpr float CursorRadiusStep = 0.2f;
enum class SelType : int8_t {
NONE,
ENFORCER,
BLOCKER
};
// For each model-part volume, store a list of statuses of
// individual facets (one of the enum values above).
std::vector<std::vector<SelType>> m_selected_facets;
std::vector<std::vector<FacetSupportType>> m_selected_facets;
// Store two vertex buffer arrays (for enforcers/blockers)
// for each model-part volume.