Merge remote-tracking branch 'origin/et_spe1698'
This commit is contained in:
commit
3e5c0bb40e
@ -23,6 +23,8 @@
|
|||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <boost/nowide/iostream.hpp>
|
#include <boost/nowide/iostream.hpp>
|
||||||
|
|
||||||
|
#include <tbb/concurrent_vector.h>
|
||||||
|
|
||||||
#include "SVG.hpp"
|
#include "SVG.hpp"
|
||||||
#include <Eigen/Dense>
|
#include <Eigen/Dense>
|
||||||
#include "GCodeWriter.hpp"
|
#include "GCodeWriter.hpp"
|
||||||
@ -1055,12 +1057,18 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
|
|||||||
// This method is used by the auto arrange function.
|
// This method is used by the auto arrange function.
|
||||||
Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const
|
Polygon ModelObject::convex_hull_2d(const Transform3d& trafo_instance) const
|
||||||
{
|
{
|
||||||
Points pts;
|
tbb::concurrent_vector<Polygon> chs;
|
||||||
for (const ModelVolume* v : volumes) {
|
chs.reserve(volumes.size());
|
||||||
if (v->is_model_part())
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, volumes.size()), [&](const tbb::blocked_range<size_t>& range) {
|
||||||
append(pts, its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast<float>(), 0.0f).points);
|
for (size_t i = range.begin(); i < range.end(); ++i) {
|
||||||
}
|
const ModelVolume* v = volumes[i];
|
||||||
return Geometry::convex_hull(std::move(pts));
|
chs.emplace_back(its_convex_hull_2d_above(v->mesh().its, (trafo_instance * v->get_matrix()).cast<float>(), 0.0f));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Polygons polygons;
|
||||||
|
polygons.assign(chs.begin(), chs.end());
|
||||||
|
return Geometry::convex_hull(polygons);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelObject::center_around_origin(bool include_modifiers)
|
void ModelObject::center_around_origin(bool include_modifiers)
|
||||||
|
@ -469,11 +469,13 @@ std::string Print::validate(std::string* warning) const
|
|||||||
return _u8L("The supplied settings will cause an empty print.");
|
return _u8L("The supplied settings will cause an empty print.");
|
||||||
|
|
||||||
if (m_config.complete_objects) {
|
if (m_config.complete_objects) {
|
||||||
if (! sequential_print_horizontal_clearance_valid(*this))
|
if (!sequential_print_horizontal_clearance_valid(*this, const_cast<Polygons*>(&m_sequential_print_clearance_contours)))
|
||||||
return _u8L("Some objects are too close; your extruder will collide with them.");
|
return _u8L("Some objects are too close; your extruder will collide with them.");
|
||||||
if (! sequential_print_vertical_clearance_valid(*this))
|
if (!sequential_print_vertical_clearance_valid(*this))
|
||||||
return _u8L("Some objects are too tall and cannot be printed without extruder collisions.");
|
return _u8L("Some objects are too tall and cannot be printed without extruder collisions.");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
const_cast<Polygons*>(&m_sequential_print_clearance_contours)->clear();
|
||||||
|
|
||||||
if (m_config.avoid_crossing_perimeters && m_config.avoid_crossing_curled_overhangs) {
|
if (m_config.avoid_crossing_perimeters && m_config.avoid_crossing_curled_overhangs) {
|
||||||
return _u8L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together.");
|
return _u8L("Avoid crossing perimeters option and avoid crossing curled overhangs option cannot be both enabled together.");
|
||||||
|
@ -609,6 +609,7 @@ public:
|
|||||||
const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; }
|
const PrintRegion& get_print_region(size_t idx) const { return *m_print_regions[idx]; }
|
||||||
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; }
|
const ToolOrdering& get_tool_ordering() const { return m_wipe_tower_data.tool_ordering; }
|
||||||
|
|
||||||
|
const Polygons& get_sequential_print_clearance_contours() const { return m_sequential_print_clearance_contours; }
|
||||||
static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr);
|
static bool sequential_print_horizontal_clearance_valid(const Print& print, Polygons* polygons = nullptr);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -658,6 +659,9 @@ private:
|
|||||||
// Estimated print time, filament consumed.
|
// Estimated print time, filament consumed.
|
||||||
PrintStatistics m_print_statistics;
|
PrintStatistics m_print_statistics;
|
||||||
|
|
||||||
|
// Cache to store sequential print clearance contours
|
||||||
|
Polygons m_sequential_print_clearance_contours;
|
||||||
|
|
||||||
// To allow GCode to set the Print's GCodeExport step status.
|
// To allow GCode to set the Print's GCodeExport step status.
|
||||||
friend class GCode;
|
friend class GCode;
|
||||||
// To allow GCodeProcessor to emit warnings.
|
// To allow GCodeProcessor to emit warnings.
|
||||||
|
@ -26,6 +26,8 @@
|
|||||||
#include <boost/nowide/cstdio.hpp>
|
#include <boost/nowide/cstdio.hpp>
|
||||||
#include <boost/predef/other/endian.h>
|
#include <boost/predef/other/endian.h>
|
||||||
|
|
||||||
|
#include <tbb/concurrent_vector.h>
|
||||||
|
|
||||||
#include <Eigen/Core>
|
#include <Eigen/Core>
|
||||||
#include <Eigen/Dense>
|
#include <Eigen/Dense>
|
||||||
|
|
||||||
@ -871,11 +873,38 @@ void its_collect_mesh_projection_points_above(const indexed_triangle_set &its, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename TransformVertex>
|
template<typename TransformVertex>
|
||||||
Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const TransformVertex &transform_fn, const float z)
|
Polygon its_convex_hull_2d_above(const indexed_triangle_set& its, const TransformVertex& transform_fn, const float z)
|
||||||
{
|
{
|
||||||
Points all_pts;
|
auto collect_mesh_projection_points_above = [&](const tbb::blocked_range<size_t>& range) {
|
||||||
its_collect_mesh_projection_points_above(its, transform_fn, z, all_pts);
|
Points pts;
|
||||||
return Geometry::convex_hull(std::move(all_pts));
|
pts.reserve(range.size() * 4); // there can be up to 4 vertices per triangle
|
||||||
|
for (size_t i = range.begin(); i < range.end(); ++i) {
|
||||||
|
const stl_triangle_vertex_indices& tri = its.indices[i];
|
||||||
|
const Vec3f tri_pts[3] = { transform_fn(its.vertices[tri(0)]), transform_fn(its.vertices[tri(1)]), transform_fn(its.vertices[tri(2)]) };
|
||||||
|
int iprev = 2;
|
||||||
|
for (int iedge = 0; iedge < 3; ++iedge) {
|
||||||
|
const Vec3f& p1 = tri_pts[iprev];
|
||||||
|
const Vec3f& p2 = tri_pts[iedge];
|
||||||
|
if ((p1.z() < z && p2.z() > z) || (p2.z() < z && p1.z() > z)) {
|
||||||
|
// Edge crosses the z plane. Calculate intersection point with the plane.
|
||||||
|
const float t = (z - p1.z()) / (p2.z() - p1.z());
|
||||||
|
pts.emplace_back(scaled<coord_t>(p1.x() + (p2.x() - p1.x()) * t), scaled<coord_t>(p1.y() + (p2.y() - p1.y()) * t));
|
||||||
|
}
|
||||||
|
if (p2.z() >= z)
|
||||||
|
pts.emplace_back(scaled<coord_t>(p2.x()), scaled<coord_t>(p2.y()));
|
||||||
|
iprev = iedge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Geometry::convex_hull(std::move(pts));
|
||||||
|
};
|
||||||
|
|
||||||
|
tbb::concurrent_vector<Polygon> chs;
|
||||||
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, its.indices.size()), [&](const tbb::blocked_range<size_t>& range) {
|
||||||
|
chs.push_back(collect_mesh_projection_points_above(range));
|
||||||
|
});
|
||||||
|
|
||||||
|
const Polygons polygons(chs.begin(), chs.end());
|
||||||
|
return Geometry::convex_hull(polygons);
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Matrix3f &m, const float z)
|
Polygon its_convex_hull_2d_above(const indexed_triangle_set &its, const Matrix3f &m, const float z)
|
||||||
|
@ -833,64 +833,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, ModelInstanceEPrintVolumeState *out_state) const
|
|
||||||
{
|
|
||||||
const Model& model = GUI::wxGetApp().plater()->model();
|
|
||||||
auto volume_below = [](GLVolume& volume) -> bool
|
|
||||||
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); };
|
|
||||||
// Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used.
|
|
||||||
auto volume_sinking = [](GLVolume& volume) -> bool
|
|
||||||
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_sinking(); };
|
|
||||||
// Cached bounding box of a volume above the print bed.
|
|
||||||
auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3
|
|
||||||
{ return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); };
|
|
||||||
// Cached 3D convex hull of a volume above the print bed.
|
|
||||||
auto volume_convex_mesh = [volume_sinking, &model](GLVolume& volume) -> const TriangleMesh&
|
|
||||||
{ return volume_sinking(volume) ? model.objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); };
|
|
||||||
|
|
||||||
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
|
|
||||||
bool contained_min_one = false;
|
|
||||||
|
|
||||||
for (GLVolume* volume : this->volumes)
|
|
||||||
if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
|
|
||||||
BuildVolume::ObjectState state;
|
|
||||||
if (volume_below(*volume))
|
|
||||||
state = BuildVolume::ObjectState::Below;
|
|
||||||
else {
|
|
||||||
switch (build_volume.type()) {
|
|
||||||
case BuildVolume::Type::Rectangle:
|
|
||||||
//FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects.
|
|
||||||
state = build_volume.volume_state_bbox(volume_bbox(*volume));
|
|
||||||
break;
|
|
||||||
case BuildVolume::Type::Circle:
|
|
||||||
case BuildVolume::Type::Convex:
|
|
||||||
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
|
||||||
case BuildVolume::Type::Custom:
|
|
||||||
state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), volume_sinking(*volume));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// Ignore, don't produce any collision.
|
|
||||||
state = BuildVolume::ObjectState::Inside;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assert(state != BuildVolume::ObjectState::Below);
|
|
||||||
}
|
|
||||||
volume->is_outside = state != BuildVolume::ObjectState::Inside;
|
|
||||||
if (volume->printable) {
|
|
||||||
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
|
|
||||||
overall_state = ModelInstancePVS_Fully_Outside;
|
|
||||||
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding)
|
|
||||||
overall_state = ModelInstancePVS_Partly_Outside;
|
|
||||||
contained_min_one |= !volume->is_outside;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (out_state != nullptr)
|
|
||||||
*out_state = overall_state;
|
|
||||||
|
|
||||||
return contained_min_one;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLVolumeCollection::reset_outside_state()
|
void GLVolumeCollection::reset_outside_state()
|
||||||
{
|
{
|
||||||
for (GLVolume* volume : this->volumes) {
|
for (GLVolume* volume : this->volumes) {
|
||||||
|
@ -450,9 +450,6 @@ public:
|
|||||||
void set_show_sinking_contours(bool show) { m_show_sinking_contours = show; }
|
void set_show_sinking_contours(bool show) { m_show_sinking_contours = show; }
|
||||||
void set_show_non_manifold_edges(bool show) { m_show_non_manifold_edges = show; }
|
void set_show_non_manifold_edges(bool show) { m_show_non_manifold_edges = show; }
|
||||||
|
|
||||||
// returns true if all the volumes are completely contained in the print volume
|
|
||||||
// returns the containment state in the given out_state, if non-null
|
|
||||||
bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
|
|
||||||
void reset_outside_state();
|
void reset_outside_state();
|
||||||
|
|
||||||
void update_colors_by_extruder(const DynamicPrintConfig* config);
|
void update_colors_by_extruder(const DynamicPrintConfig* config);
|
||||||
|
@ -880,20 +880,22 @@ void GLCanvas3D::Tooltip::render(const Vec2d& mouse_position, GLCanvas3D& canvas
|
|||||||
ImGui::PopStyleVar(2);
|
ImGui::PopStyleVar(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons)
|
void GLCanvas3D::SequentialPrintClearance::set_contours(const ContoursList& contours, bool generate_fill)
|
||||||
{
|
{
|
||||||
m_perimeter.reset();
|
m_contours.clear();
|
||||||
|
m_instances.clear();
|
||||||
m_fill.reset();
|
m_fill.reset();
|
||||||
if (polygons.empty())
|
|
||||||
|
if (contours.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_render_fill) {
|
if (generate_fill) {
|
||||||
GLModel::Geometry fill_data;
|
GLModel::Geometry fill_data;
|
||||||
fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
|
fill_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
|
||||||
fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f };
|
fill_data.color = { 0.3333f, 0.0f, 0.0f, 0.5f };
|
||||||
|
|
||||||
// vertices + indices
|
// vertices + indices
|
||||||
const ExPolygons polygons_union = union_ex(polygons);
|
const ExPolygons polygons_union = union_ex(contours.contours);
|
||||||
unsigned int vertices_counter = 0;
|
unsigned int vertices_counter = 0;
|
||||||
for (const ExPolygon& poly : polygons_union) {
|
for (const ExPolygon& poly : polygons_union) {
|
||||||
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(poly);
|
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(poly);
|
||||||
@ -906,17 +908,48 @@ void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons
|
|||||||
fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
|
fill_data.add_triangle(vertices_counter - 3, vertices_counter - 2, vertices_counter - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_fill.init_from(std::move(fill_data));
|
m_fill.init_from(std::move(fill_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_perimeter.init_from(polygons, 0.025f); // add a small positive z to avoid z-fighting
|
for (size_t i = 0; i < contours.contours.size(); ++i) {
|
||||||
|
GLModel& model = m_contours.emplace_back(GLModel());
|
||||||
|
model.init_from(contours.contours[i], 0.025f); // add a small positive z to avoid z-fighting
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contours.trafos.has_value()) {
|
||||||
|
// create the requested instances
|
||||||
|
for (const auto& instance : *contours.trafos) {
|
||||||
|
m_instances.emplace_back(instance.first, instance.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// no instances have been specified
|
||||||
|
// create one instance for every polygon
|
||||||
|
for (size_t i = 0; i < contours.contours.size(); ++i) {
|
||||||
|
m_instances.emplace_back(i, Transform3f::Identity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::SequentialPrintClearance::update_instances_trafos(const std::vector<Transform3d>& trafos)
|
||||||
|
{
|
||||||
|
if (trafos.size() == m_instances.size()) {
|
||||||
|
for (size_t i = 0; i < trafos.size(); ++i) {
|
||||||
|
m_instances[i].second = trafos[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::SequentialPrintClearance::render()
|
void GLCanvas3D::SequentialPrintClearance::render()
|
||||||
{
|
{
|
||||||
const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f };
|
const ColorRGBA FILL_COLOR = { 1.0f, 0.0f, 0.0f, 0.5f };
|
||||||
const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f };
|
const ColorRGBA NO_FILL_COLOR = { 1.0f, 1.0f, 1.0f, 0.75f };
|
||||||
|
const ColorRGBA NO_FILL_EVALUATING_COLOR = { 1.0f, 1.0f, 0.0f, 1.0f };
|
||||||
|
|
||||||
|
if (m_contours.empty() || m_instances.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
GLShaderProgram* shader = wxGetApp().get_shader("flat");
|
GLShaderProgram* shader = wxGetApp().get_shader("flat");
|
||||||
if (shader == nullptr)
|
if (shader == nullptr)
|
||||||
@ -933,9 +966,34 @@ void GLCanvas3D::SequentialPrintClearance::render()
|
|||||||
glsafe(::glEnable(GL_BLEND));
|
glsafe(::glEnable(GL_BLEND));
|
||||||
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||||
|
|
||||||
m_perimeter.set_color(m_render_fill ? FILL_COLOR : NO_FILL_COLOR);
|
if (!m_evaluating)
|
||||||
m_perimeter.render();
|
m_fill.render();
|
||||||
m_fill.render();
|
|
||||||
|
#if ENABLE_GL_CORE_PROFILE
|
||||||
|
if (OpenGLManager::get_gl_info().is_core_profile()) {
|
||||||
|
shader->stop_using();
|
||||||
|
|
||||||
|
shader = wxGetApp().get_shader("dashed_thick_lines");
|
||||||
|
if (shader == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
shader->start_using();
|
||||||
|
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
|
||||||
|
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||||
|
shader->set_uniform("viewport_size", Vec2d(double(viewport[2]), double(viewport[3])));
|
||||||
|
shader->set_uniform("width", 1.0f);
|
||||||
|
shader->set_uniform("gap_size", 0.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif // ENABLE_GL_CORE_PROFILE
|
||||||
|
glsafe(::glLineWidth(2.0f));
|
||||||
|
|
||||||
|
for (const auto& [id, trafo] : m_instances) {
|
||||||
|
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo);
|
||||||
|
assert(id < m_contours.size());
|
||||||
|
m_contours[id].set_color((!m_evaluating && m_fill.is_initialized()) ? FILL_COLOR : m_evaluating ? NO_FILL_EVALUATING_COLOR : NO_FILL_COLOR);
|
||||||
|
m_contours[id].render();
|
||||||
|
}
|
||||||
|
|
||||||
glsafe(::glDisable(GL_BLEND));
|
glsafe(::glDisable(GL_BLEND));
|
||||||
glsafe(::glEnable(GL_CULL_FACE));
|
glsafe(::glEnable(GL_CULL_FACE));
|
||||||
@ -1450,14 +1508,97 @@ void GLCanvas3D::reset_volumes()
|
|||||||
_set_warning_notification(EWarning::ObjectOutside, false);
|
_set_warning_notification(EWarning::ObjectOutside, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state() const
|
ModelInstanceEPrintVolumeState GLCanvas3D::check_volumes_outside_state(bool selection_only) const
|
||||||
{
|
{
|
||||||
ModelInstanceEPrintVolumeState state = ModelInstanceEPrintVolumeState::ModelInstancePVS_Inside;
|
ModelInstanceEPrintVolumeState state = ModelInstanceEPrintVolumeState::ModelInstancePVS_Inside;
|
||||||
if (m_initialized)
|
if (m_initialized && !m_volumes.empty())
|
||||||
m_volumes.check_outside_state(m_bed.build_volume(), &state);
|
check_volumes_outside_state(m_bed.build_volume(), &state, selection_only);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GLCanvas3D::check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state, bool selection_only) const
|
||||||
|
{
|
||||||
|
auto volume_below = [](GLVolume& volume) -> bool
|
||||||
|
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_below_printbed(); };
|
||||||
|
// Volume is partially below the print bed, thus a pre-calculated convex hull cannot be used.
|
||||||
|
auto volume_sinking = [](GLVolume& volume) -> bool
|
||||||
|
{ return volume.object_idx() != -1 && volume.volume_idx() != -1 && volume.is_sinking(); };
|
||||||
|
// Cached bounding box of a volume above the print bed.
|
||||||
|
auto volume_bbox = [volume_sinking](GLVolume& volume) -> BoundingBoxf3
|
||||||
|
{ return volume_sinking(volume) ? volume.transformed_non_sinking_bounding_box() : volume.transformed_convex_hull_bounding_box(); };
|
||||||
|
// Cached 3D convex hull of a volume above the print bed.
|
||||||
|
auto volume_convex_mesh = [this, volume_sinking](GLVolume& volume) -> const TriangleMesh&
|
||||||
|
{ return volume_sinking(volume) ? m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh() : *volume.convex_hull(); };
|
||||||
|
|
||||||
|
auto volumes_to_process_idxs = [this, selection_only]() {
|
||||||
|
std::vector<unsigned int> ret;
|
||||||
|
if (!selection_only || m_selection.is_empty()) {
|
||||||
|
ret = std::vector<unsigned int>(m_volumes.volumes.size());
|
||||||
|
std::iota(ret.begin(), ret.end(), 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const GUI::Selection::IndicesList& selected_volume_idxs = m_selection.get_volume_idxs();
|
||||||
|
ret.assign(selected_volume_idxs.begin(), selected_volume_idxs.end());
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
ModelInstanceEPrintVolumeState overall_state = ModelInstancePVS_Inside;
|
||||||
|
bool contained_min_one = false;
|
||||||
|
|
||||||
|
const std::vector<unsigned int> volumes_idxs = volumes_to_process_idxs();
|
||||||
|
|
||||||
|
for (unsigned int vol_idx : volumes_idxs) {
|
||||||
|
GLVolume* volume = m_volumes.volumes[vol_idx];
|
||||||
|
if (!volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (!volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) {
|
||||||
|
BuildVolume::ObjectState state;
|
||||||
|
if (volume_below(*volume))
|
||||||
|
state = BuildVolume::ObjectState::Below;
|
||||||
|
else {
|
||||||
|
switch (build_volume.type()) {
|
||||||
|
case BuildVolume::Type::Rectangle:
|
||||||
|
//FIXME this test does not evaluate collision of a build volume bounding box with non-convex objects.
|
||||||
|
state = build_volume.volume_state_bbox(volume_bbox(*volume));
|
||||||
|
break;
|
||||||
|
case BuildVolume::Type::Circle:
|
||||||
|
case BuildVolume::Type::Convex:
|
||||||
|
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
|
||||||
|
case BuildVolume::Type::Custom:
|
||||||
|
state = build_volume.object_state(volume_convex_mesh(*volume).its, volume->world_matrix().cast<float>(), volume_sinking(*volume));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Ignore, don't produce any collision.
|
||||||
|
state = BuildVolume::ObjectState::Inside;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(state != BuildVolume::ObjectState::Below);
|
||||||
|
}
|
||||||
|
volume->is_outside = state != BuildVolume::ObjectState::Inside;
|
||||||
|
if (volume->printable) {
|
||||||
|
if (overall_state == ModelInstancePVS_Inside && volume->is_outside)
|
||||||
|
overall_state = ModelInstancePVS_Fully_Outside;
|
||||||
|
if (overall_state == ModelInstancePVS_Fully_Outside && volume->is_outside && state == BuildVolume::ObjectState::Colliding)
|
||||||
|
overall_state = ModelInstancePVS_Partly_Outside;
|
||||||
|
contained_min_one |= !volume->is_outside;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int vol_idx = 0; vol_idx < m_volumes.volumes.size(); ++vol_idx) {
|
||||||
|
if (std::find(volumes_idxs.begin(), volumes_idxs.end(), vol_idx) == volumes_idxs.end()) {
|
||||||
|
if (!m_volumes.volumes[vol_idx]->is_outside) {
|
||||||
|
contained_min_one = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_state != nullptr)
|
||||||
|
*out_state = overall_state;
|
||||||
|
|
||||||
|
return contained_min_one;
|
||||||
|
}
|
||||||
|
|
||||||
void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx)
|
void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx)
|
||||||
{
|
{
|
||||||
if (current_printer_technology() != ptSLA)
|
if (current_printer_technology() != ptSLA)
|
||||||
@ -2462,7 +2603,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||||||
// checks for geometry outside the print volume to render it accordingly
|
// checks for geometry outside the print volume to render it accordingly
|
||||||
if (!m_volumes.empty()) {
|
if (!m_volumes.empty()) {
|
||||||
ModelInstanceEPrintVolumeState state;
|
ModelInstanceEPrintVolumeState state;
|
||||||
const bool contained_min_one = m_volumes.check_outside_state(m_bed.build_volume(), &state);
|
const bool contained_min_one = check_volumes_outside_state(m_bed.build_volume(), &state, !force_full_scene_refresh);
|
||||||
const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside);
|
const bool partlyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Partly_Outside);
|
||||||
const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside);
|
const bool fullyOut = (state == ModelInstanceEPrintVolumeState::ModelInstancePVS_Fully_Outside);
|
||||||
|
|
||||||
@ -3429,17 +3570,12 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||||||
// Not only detection of some modifiers !!!
|
// Not only detection of some modifiers !!!
|
||||||
if (evt.Dragging()) {
|
if (evt.Dragging()) {
|
||||||
GLGizmosManager::EType c = m_gizmos.get_current_type();
|
GLGizmosManager::EType c = m_gizmos.get_current_type();
|
||||||
if (current_printer_technology() == ptFFF &&
|
if (c == GLGizmosManager::EType::Move ||
|
||||||
fff_print()->config().complete_objects){
|
c == GLGizmosManager::EType::Scale ||
|
||||||
if (c == GLGizmosManager::EType::Move ||
|
c == GLGizmosManager::EType::Rotate) {
|
||||||
c == GLGizmosManager::EType::Scale ||
|
show_sinking_contours();
|
||||||
c == GLGizmosManager::EType::Rotate )
|
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects)
|
||||||
update_sequential_clearance();
|
update_sequential_clearance(true);
|
||||||
} else {
|
|
||||||
if (c == GLGizmosManager::EType::Move ||
|
|
||||||
c == GLGizmosManager::EType::Scale ||
|
|
||||||
c == GLGizmosManager::EType::Rotate)
|
|
||||||
show_sinking_contours();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (evt.LeftUp() &&
|
else if (evt.LeftUp() &&
|
||||||
@ -3642,7 +3778,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
|||||||
trafo_type.set_relative();
|
trafo_type.set_relative();
|
||||||
m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type);
|
m_selection.translate(cur_pos - m_mouse.drag.start_position_3D, trafo_type);
|
||||||
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects)
|
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects)
|
||||||
update_sequential_clearance();
|
update_sequential_clearance(false);
|
||||||
wxGetApp().obj_manipul()->set_dirty();
|
wxGetApp().obj_manipul()->set_dirty();
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
@ -3948,7 +4084,10 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
|||||||
if (wipe_tower_origin != Vec3d::Zero())
|
if (wipe_tower_origin != Vec3d::Zero())
|
||||||
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin)));
|
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin)));
|
||||||
|
|
||||||
reset_sequential_print_clearance();
|
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
|
||||||
|
update_sequential_clearance(true);
|
||||||
|
m_sequential_print_clearance.m_evaluating = true;
|
||||||
|
}
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
@ -4033,6 +4172,11 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
|||||||
if (!done.empty())
|
if (!done.empty())
|
||||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
|
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
|
||||||
|
|
||||||
|
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
|
||||||
|
update_sequential_clearance(true);
|
||||||
|
m_sequential_print_clearance.m_evaluating = true;
|
||||||
|
}
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4105,6 +4249,11 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
|
|||||||
if (!done.empty())
|
if (!done.empty())
|
||||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
|
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
|
||||||
|
|
||||||
|
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
|
||||||
|
update_sequential_clearance(true);
|
||||||
|
m_sequential_print_clearance.m_evaluating = true;
|
||||||
|
}
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4372,16 +4521,33 @@ void GLCanvas3D::mouse_up_cleanup()
|
|||||||
m_canvas->ReleaseMouse();
|
m_canvas->ReleaseMouse();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::update_sequential_clearance()
|
void GLCanvas3D::update_sequential_clearance(bool force_contours_generation)
|
||||||
{
|
{
|
||||||
if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects)
|
if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_layers_editing.is_enabled() || m_gizmos.is_dragging())
|
if (m_layers_editing.is_enabled())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
auto instance_transform_from_volumes = [this](int object_idx, int instance_idx) {
|
||||||
|
for (const GLVolume* v : m_volumes.volumes) {
|
||||||
|
if (v->object_idx() == object_idx && v->instance_idx() == instance_idx)
|
||||||
|
return v->get_instance_transformation();
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
return Geometry::Transformation();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto is_object_outside_printbed = [this](int object_idx) {
|
||||||
|
for (const GLVolume* v : m_volumes.volumes) {
|
||||||
|
if (v->object_idx() == object_idx && v->is_outside)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
// collects instance transformations from volumes
|
// collects instance transformations from volumes
|
||||||
// first define temporary cache
|
// first: define temporary cache
|
||||||
unsigned int instances_count = 0;
|
unsigned int instances_count = 0;
|
||||||
std::vector<std::vector<std::optional<Geometry::Transformation>>> instance_transforms;
|
std::vector<std::vector<std::optional<Geometry::Transformation>>> instance_transforms;
|
||||||
for (size_t obj = 0; obj < m_model->objects.size(); ++obj) {
|
for (size_t obj = 0; obj < m_model->objects.size(); ++obj) {
|
||||||
@ -4396,67 +4562,95 @@ void GLCanvas3D::update_sequential_clearance()
|
|||||||
if (instances_count == 1)
|
if (instances_count == 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// second fill temporary cache with data from volumes
|
// second: fill temporary cache with data from volumes
|
||||||
for (const GLVolume* v : m_volumes.volumes) {
|
for (const GLVolume* v : m_volumes.volumes) {
|
||||||
if (v->is_modifier || v->is_wipe_tower)
|
if (v->is_wipe_tower)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto& transform = instance_transforms[v->object_idx()][v->instance_idx()];
|
const int object_idx = v->object_idx();
|
||||||
|
const int instance_idx = v->instance_idx();
|
||||||
|
auto& transform = instance_transforms[object_idx][instance_idx];
|
||||||
if (!transform.has_value())
|
if (!transform.has_value())
|
||||||
transform = v->get_instance_transformation();
|
transform = instance_transform_from_volumes(object_idx, instance_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper function to calculate the transformation to be applied to the sequential print clearance contours
|
||||||
|
auto instance_trafo = [](const Transform3d& hull_trafo, const Geometry::Transformation& inst_trafo) {
|
||||||
|
Vec3d offset = inst_trafo.get_offset() - hull_trafo.translation();
|
||||||
|
offset.z() = 0.0;
|
||||||
|
return Geometry::translation_transform(offset) *
|
||||||
|
Geometry::rotation_transform(Geometry::rotation_diff_z(hull_trafo, inst_trafo.get_matrix()) * Vec3d::UnitZ());
|
||||||
|
};
|
||||||
|
|
||||||
// calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid())
|
// calculates objects 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid())
|
||||||
// this is done only the first time this method is called while moving the mouse,
|
// this is done only the first time this method is called while moving the mouse,
|
||||||
// the results are then cached for following displacements
|
// the results are then cached for following displacements
|
||||||
if (m_sequential_print_clearance_first_displacement) {
|
if (force_contours_generation || m_sequential_print_clearance_first_displacement) {
|
||||||
m_sequential_print_clearance.m_hull_2d_cache.clear();
|
m_sequential_print_clearance.m_evaluating = false;
|
||||||
|
m_sequential_print_clearance.m_hulls_2d_cache.clear();
|
||||||
const float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
|
const float shrink_factor = static_cast<float>(scale_(0.5 * fff_print()->config().extruder_clearance_radius.value - EPSILON));
|
||||||
const double mitter_limit = scale_(0.1);
|
const double mitter_limit = scale_(0.1);
|
||||||
m_sequential_print_clearance.m_hull_2d_cache.reserve(m_model->objects.size());
|
m_sequential_print_clearance.m_hulls_2d_cache.reserve(m_model->objects.size());
|
||||||
for (size_t i = 0; i < m_model->objects.size(); ++i) {
|
for (size_t i = 0; i < m_model->objects.size(); ++i) {
|
||||||
ModelObject* model_object = m_model->objects[i];
|
ModelObject* model_object = m_model->objects[i];
|
||||||
ModelInstance* model_instance0 = model_object->instances.front();
|
Geometry::Transformation trafo = instance_transform_from_volumes((int)i, 0);
|
||||||
Geometry::Transformation trafo = model_instance0->get_transformation();
|
trafo.set_offset({ 0.0, 0.0, trafo.get_offset().z() });
|
||||||
trafo.set_offset({ 0.0, 0.0, model_instance0->get_offset().z() });
|
Pointf3s& new_hull_2d = m_sequential_print_clearance.m_hulls_2d_cache.emplace_back(std::make_pair(Pointf3s(), trafo.get_matrix())).first;
|
||||||
const Polygon hull_2d = offset(model_object->convex_hull_2d(trafo.get_matrix()),
|
if (is_object_outside_printbed((int)i))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Polygon hull_2d = model_object->convex_hull_2d(trafo.get_matrix());
|
||||||
|
if (!hull_2d.empty()) {
|
||||||
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||||||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||||||
shrink_factor,
|
const Polygons offset_res = offset(hull_2d, shrink_factor, jtRound, mitter_limit);
|
||||||
jtRound, mitter_limit).front();
|
if (!offset_res.empty())
|
||||||
|
hull_2d = offset_res.front();
|
||||||
|
}
|
||||||
|
|
||||||
Pointf3s& cache_hull_2d = m_sequential_print_clearance.m_hull_2d_cache.emplace_back(Pointf3s());
|
new_hull_2d.reserve(hull_2d.points.size());
|
||||||
cache_hull_2d.reserve(hull_2d.points.size());
|
|
||||||
const Transform3d inv_trafo = trafo.get_matrix().inverse();
|
const Transform3d inv_trafo = trafo.get_matrix().inverse();
|
||||||
for (const Point& p : hull_2d.points) {
|
for (const Point& p : hull_2d.points) {
|
||||||
cache_hull_2d.emplace_back(inv_trafo * Vec3d(unscale<double>(p.x()), unscale<double>(p.y()), 0.0));
|
new_hull_2d.emplace_back(Vec3d(unscale<double>(p.x()), unscale<double>(p.y()), 0.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContoursList contours;
|
||||||
|
contours.contours.reserve(instance_transforms.size());
|
||||||
|
contours.trafos = std::vector<std::pair<size_t, Transform3d>>();
|
||||||
|
(*contours.trafos).reserve(instances_count);
|
||||||
|
for (size_t i = 0; i < instance_transforms.size(); ++i) {
|
||||||
|
const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i];
|
||||||
|
Points hull_pts;
|
||||||
|
hull_pts.reserve(hull.size());
|
||||||
|
for (size_t j = 0; j < hull.size(); ++j) {
|
||||||
|
hull_pts.emplace_back(scaled<double>(hull[j].x()), scaled<double>(hull[j].y()));
|
||||||
|
}
|
||||||
|
contours.contours.emplace_back(Geometry::convex_hull(std::move(hull_pts)));
|
||||||
|
|
||||||
|
const auto& instances = instance_transforms[i];
|
||||||
|
for (const auto& instance : instances) {
|
||||||
|
(*contours.trafos).emplace_back(i, instance_trafo(hull_trafo, *instance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_sequential_print_clearance_contours(contours, false);
|
||||||
m_sequential_print_clearance_first_displacement = false;
|
m_sequential_print_clearance_first_displacement = false;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
// calculates instances 2d hulls (see also: Print::sequential_print_horizontal_clearance_valid())
|
if (!m_sequential_print_clearance.empty()) {
|
||||||
Polygons polygons;
|
std::vector<Transform3d> trafos;
|
||||||
polygons.reserve(instances_count);
|
trafos.reserve(instances_count);
|
||||||
for (size_t i = 0; i < instance_transforms.size(); ++i) {
|
for (size_t i = 0; i < instance_transforms.size(); ++i) {
|
||||||
const auto& instances = instance_transforms[i];
|
const auto& [hull, hull_trafo] = m_sequential_print_clearance.m_hulls_2d_cache[i];
|
||||||
for (const auto& instance : instances) {
|
const auto& instances = instance_transforms[i];
|
||||||
const Transform3d& trafo = instance->get_matrix();
|
for (const auto& instance : instances) {
|
||||||
const Pointf3s& hull_2d = m_sequential_print_clearance.m_hull_2d_cache[i];
|
trafos.emplace_back(instance_trafo(hull_trafo, *instance));
|
||||||
Points inst_pts;
|
}
|
||||||
inst_pts.reserve(hull_2d.size());
|
|
||||||
for (size_t j = 0; j < hull_2d.size(); ++j) {
|
|
||||||
const Vec3d p = trafo * hull_2d[j];
|
|
||||||
inst_pts.emplace_back(scaled<double>(p.x()), scaled<double>(p.y()));
|
|
||||||
}
|
}
|
||||||
polygons.emplace_back(Geometry::convex_hull(std::move(inst_pts)));
|
m_sequential_print_clearance.update_instances_trafos(trafos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sends instances 2d hulls to be rendered
|
|
||||||
set_sequential_print_clearance_visible(true);
|
|
||||||
set_sequential_print_clearance_render_fill(false);
|
|
||||||
set_sequential_print_clearance_polygons(polygons);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::is_object_sinking(int object_idx) const
|
bool GLCanvas3D::is_object_sinking(int object_idx) const
|
||||||
@ -5936,7 +6130,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_requires_check_outside_state) {
|
if (m_requires_check_outside_state) {
|
||||||
m_volumes.check_outside_state(build_volume, nullptr);
|
check_volumes_outside_state(build_volume, nullptr);
|
||||||
m_requires_check_outside_state = false;
|
m_requires_check_outside_state = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6031,15 +6225,20 @@ void GLCanvas3D::_render_selection()
|
|||||||
|
|
||||||
void GLCanvas3D::_render_sequential_clearance()
|
void GLCanvas3D::_render_sequential_clearance()
|
||||||
{
|
{
|
||||||
if (m_layers_editing.is_enabled() || m_gizmos.is_dragging())
|
if (current_printer_technology() != ptFFF || !fff_print()->config().complete_objects)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_layers_editing.is_enabled())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (m_gizmos.get_current_type())
|
switch (m_gizmos.get_current_type())
|
||||||
{
|
{
|
||||||
case GLGizmosManager::EType::Flatten:
|
case GLGizmosManager::EType::Flatten:
|
||||||
case GLGizmosManager::EType::Cut:
|
case GLGizmosManager::EType::Cut:
|
||||||
case GLGizmosManager::EType::Hollow:
|
case GLGizmosManager::EType::MmuSegmentation:
|
||||||
case GLGizmosManager::EType::SlaSupports:
|
case GLGizmosManager::EType::Measure:
|
||||||
|
case GLGizmosManager::EType::Emboss:
|
||||||
|
case GLGizmosManager::EType::Simplify:
|
||||||
case GLGizmosManager::EType::FdmSupports:
|
case GLGizmosManager::EType::FdmSupports:
|
||||||
case GLGizmosManager::EType::Seam: { return; }
|
case GLGizmosManager::EType::Seam: { return; }
|
||||||
default: { break; }
|
default: { break; }
|
||||||
|
@ -619,23 +619,35 @@ public:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ContoursList
|
||||||
|
{
|
||||||
|
// list of unique contours
|
||||||
|
Polygons contours;
|
||||||
|
// if defined: list of transforms to apply to contours
|
||||||
|
std::optional<std::vector<std::pair<size_t, Transform3d>>> trafos;
|
||||||
|
|
||||||
|
bool empty() const { return contours.empty(); }
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void load_arrange_settings();
|
void load_arrange_settings();
|
||||||
|
|
||||||
class SequentialPrintClearance
|
class SequentialPrintClearance
|
||||||
{
|
{
|
||||||
GLModel m_fill;
|
GLModel m_fill;
|
||||||
GLModel m_perimeter;
|
// list of unique contours
|
||||||
bool m_render_fill{ true };
|
std::vector<GLModel> m_contours;
|
||||||
bool m_visible{ false };
|
// list of transforms used to render the contours
|
||||||
|
std::vector<std::pair<size_t, Transform3d>> m_instances;
|
||||||
|
bool m_evaluating{ false };
|
||||||
|
|
||||||
std::vector<Pointf3s> m_hull_2d_cache;
|
std::vector<std::pair<Pointf3s, Transform3d>> m_hulls_2d_cache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void set_polygons(const Polygons& polygons);
|
void set_contours(const ContoursList& contours, bool generate_fill);
|
||||||
void set_render_fill(bool render_fill) { m_render_fill = render_fill; }
|
void update_instances_trafos(const std::vector<Transform3d>& trafos);
|
||||||
void set_visible(bool visible) { m_visible = visible; }
|
|
||||||
void render();
|
void render();
|
||||||
|
bool empty() const { return m_contours.empty(); }
|
||||||
|
|
||||||
friend class GLCanvas3D;
|
friend class GLCanvas3D;
|
||||||
};
|
};
|
||||||
@ -725,7 +737,10 @@ public:
|
|||||||
unsigned int get_volumes_count() const;
|
unsigned int get_volumes_count() const;
|
||||||
const GLVolumeCollection& get_volumes() const { return m_volumes; }
|
const GLVolumeCollection& get_volumes() const { return m_volumes; }
|
||||||
void reset_volumes();
|
void reset_volumes();
|
||||||
ModelInstanceEPrintVolumeState check_volumes_outside_state() const;
|
ModelInstanceEPrintVolumeState check_volumes_outside_state(bool selection_only = true) const;
|
||||||
|
// returns true if all the volumes are completely contained in the print volume
|
||||||
|
// returns the containment state in the given out_state, if non-null
|
||||||
|
bool check_volumes_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state, bool selection_only = true) const;
|
||||||
|
|
||||||
void init_gcode_viewer() { m_gcode_viewer.init(); }
|
void init_gcode_viewer() { m_gcode_viewer.init(); }
|
||||||
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }
|
void reset_gcode_toolpaths() { m_gcode_viewer.reset(); }
|
||||||
@ -959,24 +974,24 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void reset_sequential_print_clearance() {
|
void reset_sequential_print_clearance() {
|
||||||
m_sequential_print_clearance.set_visible(false);
|
m_sequential_print_clearance.m_evaluating = false;
|
||||||
m_sequential_print_clearance.set_render_fill(false);
|
m_sequential_print_clearance.set_contours(ContoursList(), false);
|
||||||
m_sequential_print_clearance.set_polygons(Polygons());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_sequential_print_clearance_visible(bool visible) {
|
void set_sequential_print_clearance_contours(const ContoursList& contours, bool generate_fill) {
|
||||||
m_sequential_print_clearance.set_visible(visible);
|
m_sequential_print_clearance.set_contours(contours, generate_fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_sequential_print_clearance_render_fill(bool render_fill) {
|
bool is_sequential_print_clearance_empty() const {
|
||||||
m_sequential_print_clearance.set_render_fill(render_fill);
|
return m_sequential_print_clearance.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_sequential_print_clearance_polygons(const Polygons& polygons) {
|
bool is_sequential_print_clearance_evaluating() const {
|
||||||
m_sequential_print_clearance.set_polygons(polygons);
|
return m_sequential_print_clearance.m_evaluating;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_sequential_clearance();
|
void update_sequential_clearance(bool force_contours_generation);
|
||||||
|
void set_sequential_clearance_as_evaluating() { m_sequential_print_clearance.m_evaluating = true; }
|
||||||
|
|
||||||
const Print* fff_print() const;
|
const Print* fff_print() const;
|
||||||
const SLAPrint* sla_print() const;
|
const SLAPrint* sla_print() const;
|
||||||
|
@ -596,6 +596,38 @@ void GLModel::init_from(const indexed_triangle_set& its)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLModel::init_from(const Polygon& polygon, float z)
|
||||||
|
{
|
||||||
|
if (is_initialized()) {
|
||||||
|
// call reset() if you want to reuse this model
|
||||||
|
assert(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Geometry& data = m_render_data.geometry;
|
||||||
|
data.format = { Geometry::EPrimitiveType::Lines, Geometry::EVertexLayout::P3 };
|
||||||
|
|
||||||
|
const size_t segments_count = polygon.points.size();
|
||||||
|
data.reserve_vertices(2 * segments_count);
|
||||||
|
data.reserve_indices(2 * segments_count);
|
||||||
|
|
||||||
|
// vertices + indices
|
||||||
|
unsigned int vertices_counter = 0;
|
||||||
|
for (size_t i = 0; i < segments_count; ++i) {
|
||||||
|
const Point& p0 = polygon.points[i];
|
||||||
|
const Point& p1 = (i == segments_count - 1) ? polygon.points.front() : polygon.points[i + 1];
|
||||||
|
data.add_vertex(Vec3f(unscale<float>(p0.x()), unscale<float>(p0.y()), z));
|
||||||
|
data.add_vertex(Vec3f(unscale<float>(p1.x()), unscale<float>(p1.y()), z));
|
||||||
|
vertices_counter += 2;
|
||||||
|
data.add_line(vertices_counter - 2, vertices_counter - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update bounding box
|
||||||
|
for (size_t i = 0; i < vertices_count(); ++i) {
|
||||||
|
m_bounding_box.merge(data.extract_position_3(i).cast<double>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GLModel::init_from(const Polygons& polygons, float z)
|
void GLModel::init_from(const Polygons& polygons, float z)
|
||||||
{
|
{
|
||||||
if (is_initialized()) {
|
if (is_initialized()) {
|
||||||
|
@ -227,6 +227,7 @@ namespace GUI {
|
|||||||
void init_from(const TriangleMesh& mesh);
|
void init_from(const TriangleMesh& mesh);
|
||||||
#endif // ENABLE_SMOOTH_NORMALS
|
#endif // ENABLE_SMOOTH_NORMALS
|
||||||
void init_from(const indexed_triangle_set& its);
|
void init_from(const indexed_triangle_set& its);
|
||||||
|
void init_from(const Polygon& polygon, float z);
|
||||||
void init_from(const Polygons& polygons, float z);
|
void init_from(const Polygons& polygons, float z);
|
||||||
bool init_from_file(const std::string& filename);
|
bool init_from_file(const std::string& filename);
|
||||||
|
|
||||||
|
@ -4016,8 +4016,10 @@ void ObjectList::update_selections_on_canvas()
|
|||||||
selection.add_volumes(mode, volume_idxs, single_selection);
|
selection.add_volumes(mode, volume_idxs, single_selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
wxGetApp().plater()->canvas3D()->update_gizmos_on_off_state();
|
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||||
wxGetApp().plater()->canvas3D()->render();
|
canvas->update_gizmos_on_off_state();
|
||||||
|
canvas->check_volumes_outside_state();
|
||||||
|
canvas->render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::select_item(const wxDataViewItem& item)
|
void ObjectList::select_item(const wxDataViewItem& item)
|
||||||
|
@ -859,13 +859,13 @@ wxString ObjectManipulation::coordinate_type_str(ECoordinatesType type)
|
|||||||
#if ENABLE_OBJECT_MANIPULATION_DEBUG
|
#if ENABLE_OBJECT_MANIPULATION_DEBUG
|
||||||
void ObjectManipulation::render_debug_window()
|
void ObjectManipulation::render_debug_window()
|
||||||
{
|
{
|
||||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||||
// ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
// ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
||||||
imgui.begin(std::string("ObjectManipulation"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize);
|
imgui.begin(std::string("ObjectManipulation"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize);
|
||||||
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Coordinates type");
|
imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Coordinates type");
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
imgui.text(coordinate_type_str(m_coordinates_type));
|
imgui.text(coordinate_type_str(m_coordinates_type));
|
||||||
imgui.end();
|
imgui.end();
|
||||||
}
|
}
|
||||||
#endif // ENABLE_OBJECT_MANIPULATION_DEBUG
|
#endif // ENABLE_OBJECT_MANIPULATION_DEBUG
|
||||||
|
|
||||||
|
@ -355,7 +355,7 @@ void ArrangeJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
|||||||
ap.apply();
|
ap.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_plater->update();
|
m_plater->update((unsigned int)Plater::UpdateParams::FORCE_FULL_SCREEN_REFRESH);
|
||||||
wxGetApp().obj_manipul()->set_dirty();
|
wxGetApp().obj_manipul()->set_dirty();
|
||||||
|
|
||||||
if (!m_unarranged.empty()) {
|
if (!m_unarranged.empty()) {
|
||||||
|
@ -1765,11 +1765,6 @@ struct Plater::priv
|
|||||||
void render_project_state_debug_window() const { dirty_state.render_debug_window(); }
|
void render_project_state_debug_window() const { dirty_state.render_debug_window(); }
|
||||||
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
|
||||||
enum class UpdateParams {
|
|
||||||
FORCE_FULL_SCREEN_REFRESH = 1,
|
|
||||||
FORCE_BACKGROUND_PROCESSING_UPDATE = 2,
|
|
||||||
POSTPONE_VALIDATION_ERROR_MESSAGE = 4,
|
|
||||||
};
|
|
||||||
void update(unsigned int flags = 0);
|
void update(unsigned int flags = 0);
|
||||||
void select_view(const std::string& direction);
|
void select_view(const std::string& direction);
|
||||||
void select_view_3D(const std::string& name);
|
void select_view_3D(const std::string& name);
|
||||||
@ -3055,8 +3050,10 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx)
|
|||||||
sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx);
|
sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx);
|
||||||
|
|
||||||
model.delete_object(obj_idx);
|
model.delete_object(obj_idx);
|
||||||
|
|
||||||
update();
|
update();
|
||||||
object_list_changed();
|
object_list_changed();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3253,7 +3250,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
|||||||
if (view3D->is_layers_editing_enabled())
|
if (view3D->is_layers_editing_enabled())
|
||||||
view3D->get_wxglcanvas()->Refresh();
|
view3D->get_wxglcanvas()->Refresh();
|
||||||
|
|
||||||
if (background_process.empty())
|
if (invalidated == Print::APPLY_STATUS_CHANGED || background_process.empty())
|
||||||
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
||||||
|
|
||||||
if (invalidated == Print::APPLY_STATUS_INVALIDATED) {
|
if (invalidated == Print::APPLY_STATUS_INVALIDATED) {
|
||||||
@ -3292,29 +3289,43 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
|||||||
// or hide the old one.
|
// or hide the old one.
|
||||||
process_validation_warning(warning);
|
process_validation_warning(warning);
|
||||||
if (printer_technology == ptFFF) {
|
if (printer_technology == ptFFF) {
|
||||||
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
GLCanvas3D* canvas = view3D->get_canvas3d();
|
||||||
view3D->get_canvas3d()->set_as_dirty();
|
canvas->reset_sequential_print_clearance();
|
||||||
view3D->get_canvas3d()->request_extra_frame();
|
canvas->set_as_dirty();
|
||||||
|
canvas->request_extra_frame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// The print is not valid.
|
// The print is not valid.
|
||||||
// Show error as notification.
|
// Show error as notification.
|
||||||
notification_manager->push_validate_error_notification(err);
|
notification_manager->push_validate_error_notification(err);
|
||||||
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
|
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
|
||||||
if (printer_technology == ptFFF) {
|
if (printer_technology == ptFFF) {
|
||||||
const Print* print = background_process.fff_print();
|
GLCanvas3D* canvas = view3D->get_canvas3d();
|
||||||
Polygons polygons;
|
if (canvas->is_sequential_print_clearance_empty() || canvas->is_sequential_print_clearance_evaluating()) {
|
||||||
if (print->config().complete_objects)
|
GLCanvas3D::ContoursList contours;
|
||||||
Print::sequential_print_horizontal_clearance_valid(*print, &polygons);
|
contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours();
|
||||||
view3D->get_canvas3d()->set_sequential_print_clearance_visible(true);
|
canvas->set_sequential_print_clearance_contours(contours, true);
|
||||||
view3D->get_canvas3d()->set_sequential_print_clearance_render_fill(true);
|
canvas->set_as_dirty();
|
||||||
view3D->get_canvas3d()->set_sequential_print_clearance_polygons(polygons);
|
canvas->request_extra_frame();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (invalidated == Print::APPLY_STATUS_UNCHANGED && !background_process.empty()) {
|
if (invalidated == Print::APPLY_STATUS_UNCHANGED && !background_process.empty()) {
|
||||||
|
if (printer_technology == ptFFF) {
|
||||||
|
// Object manipulation with gizmos may end up in a null transformation.
|
||||||
|
// In this case, we need to trigger the completion of the sequential print clearance contours evaluation
|
||||||
|
GLCanvas3D* canvas = view3D->get_canvas3d();
|
||||||
|
if (canvas->is_sequential_print_clearance_evaluating()) {
|
||||||
|
GLCanvas3D::ContoursList contours;
|
||||||
|
contours.contours = background_process.fff_print()->get_sequential_print_clearance_contours();
|
||||||
|
canvas->set_sequential_print_clearance_contours(contours, true);
|
||||||
|
canvas->set_as_dirty();
|
||||||
|
canvas->request_extra_frame();
|
||||||
|
}
|
||||||
|
}
|
||||||
std::string warning;
|
std::string warning;
|
||||||
std::string err = background_process.validate(&warning);
|
std::string err = background_process.validate(&warning);
|
||||||
if (!err.empty())
|
if (!err.empty())
|
||||||
@ -4416,7 +4427,6 @@ void Plater::priv::on_update_geometry(Vec3dsEvent<2>&)
|
|||||||
|
|
||||||
void Plater::priv::on_3dcanvas_mouse_dragging_started(SimpleEvent&)
|
void Plater::priv::on_3dcanvas_mouse_dragging_started(SimpleEvent&)
|
||||||
{
|
{
|
||||||
view3D->get_canvas3d()->reset_sequential_print_clearance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the scene from the background processing,
|
// Update the scene from the background processing,
|
||||||
@ -6005,7 +6015,7 @@ bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/*
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::update() { p->update(); }
|
void Plater::update(unsigned int flags) { p->update(flags); }
|
||||||
|
|
||||||
Worker &Plater::get_ui_job_worker() { return p->m_worker; }
|
Worker &Plater::get_ui_job_worker() { return p->m_worker; }
|
||||||
|
|
||||||
|
@ -185,7 +185,12 @@ public:
|
|||||||
|
|
||||||
const wxString& get_last_loaded_gcode() const { return m_last_loaded_gcode; }
|
const wxString& get_last_loaded_gcode() const { return m_last_loaded_gcode; }
|
||||||
|
|
||||||
void update();
|
enum class UpdateParams {
|
||||||
|
FORCE_FULL_SCREEN_REFRESH = 1,
|
||||||
|
FORCE_BACKGROUND_PROCESSING_UPDATE = 2,
|
||||||
|
POSTPONE_VALIDATION_ERROR_MESSAGE = 4,
|
||||||
|
};
|
||||||
|
void update(unsigned int flags = 0);
|
||||||
|
|
||||||
// Get the worker handling the UI jobs (arrange, fill bed, etc...)
|
// Get the worker handling the UI jobs (arrange, fill bed, etc...)
|
||||||
// Here is an example of starting up an ad-hoc job:
|
// Here is an example of starting up an ad-hoc job:
|
||||||
|
@ -1538,6 +1538,11 @@ void Selection::erase()
|
|||||||
wxGetApp().obj_list()->delete_from_model_and_list(items);
|
wxGetApp().obj_list()->delete_from_model_and_list(items);
|
||||||
ensure_not_below_bed();
|
ensure_not_below_bed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
|
||||||
|
canvas->set_sequential_clearance_as_evaluating();
|
||||||
|
canvas->set_as_dirty();
|
||||||
|
canvas->request_extra_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Selection::render(float scale_factor)
|
void Selection::render(float scale_factor)
|
||||||
|
Loading…
Reference in New Issue
Block a user