Merge branch 'master' into fs_emboss
This commit is contained in:
commit
ae5f594e1e
@ -114,6 +114,7 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print,
|
|||||||
clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true);
|
clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true);
|
||||||
// Execute union operation to construct polytree
|
// Execute union operation to construct polytree
|
||||||
ClipperLib_Z::PolyTree islands_polytree;
|
ClipperLib_Z::PolyTree islands_polytree;
|
||||||
|
//FIXME likely pftNonZero or ptfPositive would be better. Why are we using ptfEvenOdd for Unions?
|
||||||
clipper.Execute(ClipperLib_Z::ctUnion, islands_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
|
clipper.Execute(ClipperLib_Z::ctUnion, islands_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
|
||||||
|
|
||||||
std::unordered_set<size_t> processed_objects_idx;
|
std::unordered_set<size_t> processed_objects_idx;
|
||||||
@ -486,7 +487,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
|||||||
clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true);
|
clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true);
|
||||||
clipper.AddPaths(input_clip, ClipperLib_Z::ptClip, true);
|
clipper.AddPaths(input_clip, ClipperLib_Z::ptClip, true);
|
||||||
// perform operation
|
// perform operation
|
||||||
clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
|
clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second, trim the extrusion loops with the trimming regions.
|
// Second, trim the extrusion loops with the trimming regions.
|
||||||
@ -515,7 +516,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
|||||||
clipper.AddPaths(trimming, ClipperLib_Z::ptClip, true);
|
clipper.AddPaths(trimming, ClipperLib_Z::ptClip, true);
|
||||||
// perform operation
|
// perform operation
|
||||||
ClipperLib_Z::PolyTree loops_trimmed_tree;
|
ClipperLib_Z::PolyTree loops_trimmed_tree;
|
||||||
clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
|
clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
|
||||||
ClipperLib_Z::PolyTreeToPaths(loops_trimmed_tree, loops_trimmed);
|
ClipperLib_Z::PolyTreeToPaths(loops_trimmed_tree, loops_trimmed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1244,8 +1244,8 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->enum_values.push_back("external");
|
def->enum_values.push_back("external");
|
||||||
def->enum_values.push_back("all");
|
def->enum_values.push_back("all");
|
||||||
def->enum_labels.push_back(L("None"));
|
def->enum_labels.push_back(L("None"));
|
||||||
def->enum_labels.push_back(L("External perimeters"));
|
def->enum_labels.push_back(L("Outside walls"));
|
||||||
def->enum_labels.push_back(L("All perimeters"));
|
def->enum_labels.push_back(L("All walls"));
|
||||||
def->mode = comSimple;
|
def->mode = comSimple;
|
||||||
def->set_default_value(new ConfigOptionEnum<FuzzySkinType>(FuzzySkinType::None));
|
def->set_default_value(new ConfigOptionEnum<FuzzySkinType>(FuzzySkinType::None));
|
||||||
|
|
||||||
@ -3970,10 +3970,16 @@ std::string validate(const FullPrintConfig &cfg)
|
|||||||
if (em <= 0)
|
if (em <= 0)
|
||||||
return "Invalid value for --extrusion-multiplier";
|
return "Invalid value for --extrusion-multiplier";
|
||||||
|
|
||||||
|
// The following test was commented out after 482841b, see also https://github.com/prusa3d/PrusaSlicer/pull/6743.
|
||||||
|
// The backend should now handle this case correctly. I.e., zero default_acceleration behaves as if all others
|
||||||
|
// were zero too. This is now consistent with what the UI said would happen.
|
||||||
|
// The UI already grays the fields out, there is no more reason to reject it here. This function validates the
|
||||||
|
// config before exporting, leaving this check in would mean that config would be rejected before export
|
||||||
|
// (although both the UI and the backend handle it).
|
||||||
// --default-acceleration
|
// --default-acceleration
|
||||||
if ((cfg.perimeter_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.first_layer_acceleration != 0.) &&
|
//if ((cfg.perimeter_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.first_layer_acceleration != 0.) &&
|
||||||
cfg.default_acceleration == 0.)
|
// cfg.default_acceleration == 0.)
|
||||||
return "Invalid zero value for --default-acceleration when using other acceleration settings";
|
// return "Invalid zero value for --default-acceleration when using other acceleration settings";
|
||||||
|
|
||||||
// --spiral-vase
|
// --spiral-vase
|
||||||
if (cfg.spiral_vase) {
|
if (cfg.spiral_vase) {
|
||||||
|
@ -133,7 +133,8 @@ ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta)
|
|||||||
|
|
||||||
Polygons offset_waffle_style(const ConcaveHull &hull, coord_t delta)
|
Polygons offset_waffle_style(const ConcaveHull &hull, coord_t delta)
|
||||||
{
|
{
|
||||||
Polygons res = closing(hull.polygons(), 2 * delta, delta, ClipperLib::jtRound);
|
auto arc_tolerance = scaled<double>(0.01);
|
||||||
|
Polygons res = closing(hull.polygons(), 2 * delta, delta, ClipperLib::jtRound, arc_tolerance);
|
||||||
|
|
||||||
auto it = std::remove_if(res.begin(), res.end(), [](Polygon &p) { return p.is_clockwise(); });
|
auto it = std::remove_if(res.begin(), res.end(), [](Polygon &p) { return p.is_clockwise(); });
|
||||||
res.erase(it, res.end());
|
res.erase(it, res.end());
|
||||||
|
@ -128,13 +128,13 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
|||||||
const Vec3f& source, float radius,
|
const Vec3f& source, float radius,
|
||||||
CursorType cursor_type, EnforcerBlockerType new_state,
|
CursorType cursor_type, EnforcerBlockerType new_state,
|
||||||
const Transform3d& trafo, const Transform3d& trafo_no_translate,
|
const Transform3d& trafo, const Transform3d& trafo_no_translate,
|
||||||
bool triangle_splitting, float highlight_by_angle_deg)
|
bool triangle_splitting, const ClippingPlane &clp, float highlight_by_angle_deg)
|
||||||
{
|
{
|
||||||
assert(facet_start < m_orig_size_indices);
|
assert(facet_start < m_orig_size_indices);
|
||||||
|
|
||||||
// Save current cursor center, squared radius and camera direction, so we don't
|
// Save current cursor center, squared radius and camera direction, so we don't
|
||||||
// have to pass it around.
|
// have to pass it around.
|
||||||
m_cursor = Cursor(hit, source, radius, cursor_type, trafo);
|
m_cursor = Cursor(hit, source, radius, cursor_type, trafo, clp);
|
||||||
|
|
||||||
// In case user changed cursor size since last time, update triangle edge limit.
|
// In case user changed cursor size since last time, update triangle edge limit.
|
||||||
// It is necessary to compare the internal radius in m_cursor! radius is in
|
// It is necessary to compare the internal radius in m_cursor! radius is in
|
||||||
@ -172,15 +172,23 @@ void TriangleSelector::select_patch(const Vec3f& hit, int facet_start,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start,
|
bool TriangleSelector::is_facet_clipped(int facet_idx, const ClippingPlane &clp) const
|
||||||
const Transform3d& trafo_no_translate,
|
{
|
||||||
float seed_fill_angle, float highlight_by_angle_deg,
|
for (int vert_idx : m_triangles[facet_idx].verts_idxs)
|
||||||
|
if (clp.is_active() && clp.is_mesh_point_clipped(m_vertices[vert_idx].v))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, const Transform3d& trafo_no_translate,
|
||||||
|
const ClippingPlane &clp, float seed_fill_angle, float highlight_by_angle_deg,
|
||||||
bool force_reselection)
|
bool force_reselection)
|
||||||
{
|
{
|
||||||
assert(facet_start < m_orig_size_indices);
|
assert(facet_start < m_orig_size_indices);
|
||||||
|
|
||||||
// Recompute seed fill only if the cursor is pointing on facet unselected by seed fill.
|
// Recompute seed fill only if the cursor is pointing on facet unselected by seed fill or a clipping plane is active.
|
||||||
if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection)
|
if (int start_facet_idx = select_unsplit_triangle(hit, facet_start); start_facet_idx >= 0 && m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection && !clp.is_active())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this->seed_fill_unselect_all_triangles();
|
this->seed_fill_unselect_all_triangles();
|
||||||
@ -215,7 +223,7 @@ void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_st
|
|||||||
// Propagate over the original triangles.
|
// Propagate over the original triangles.
|
||||||
for (int neighbor_idx : m_neighbors[current_facet]) {
|
for (int neighbor_idx : m_neighbors[current_facet]) {
|
||||||
assert(neighbor_idx >= -1);
|
assert(neighbor_idx >= -1);
|
||||||
if (neighbor_idx >= 0 && !visited[neighbor_idx]) {
|
if (neighbor_idx >= 0 && !visited[neighbor_idx] && !is_facet_clipped(neighbor_idx, clp)) {
|
||||||
// Check if neighbour_facet_idx is satisfies angle in seed_fill_angle and append it to facet_queue if it do.
|
// Check if neighbour_facet_idx is satisfies angle in seed_fill_angle and append it to facet_queue if it do.
|
||||||
const Vec3f &n1 = m_face_normals[m_triangles[neighbor_idx].source_triangle];
|
const Vec3f &n1 = m_face_normals[m_triangles[neighbor_idx].source_triangle];
|
||||||
const Vec3f &n2 = m_face_normals[m_triangles[current_facet].source_triangle];
|
const Vec3f &n2 = m_face_normals[m_triangles[current_facet].source_triangle];
|
||||||
@ -331,12 +339,12 @@ void TriangleSelector::append_touching_edges(int itriangle, int vertexi, int ver
|
|||||||
process_subtriangle(touching.second, Partition::Second);
|
process_subtriangle(touching.second, Partition::Second);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, bool propagate, bool force_reselection)
|
void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_start, const ClippingPlane &clp, bool propagate, bool force_reselection)
|
||||||
{
|
{
|
||||||
int start_facet_idx = select_unsplit_triangle(hit, facet_start);
|
int start_facet_idx = select_unsplit_triangle(hit, facet_start);
|
||||||
assert(start_facet_idx != -1);
|
assert(start_facet_idx != -1);
|
||||||
// Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill.
|
// Recompute bucket fill only if the cursor is pointing on facet unselected by bucket fill or a clipping plane is active.
|
||||||
if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection))
|
if (start_facet_idx == -1 || (m_triangles[start_facet_idx].is_selected_by_seed_fill() && !force_reselection && !clp.is_active()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
assert(!m_triangles[start_facet_idx].is_split());
|
assert(!m_triangles[start_facet_idx].is_split());
|
||||||
@ -379,7 +387,7 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_
|
|||||||
|
|
||||||
std::vector<int> touching_triangles = get_all_touching_triangles(current_facet, neighbors[current_facet], neighbors_propagated[current_facet]);
|
std::vector<int> touching_triangles = get_all_touching_triangles(current_facet, neighbors[current_facet], neighbors_propagated[current_facet]);
|
||||||
for(const int tr_idx : touching_triangles) {
|
for(const int tr_idx : touching_triangles) {
|
||||||
if (tr_idx < 0 || visited[tr_idx] || m_triangles[tr_idx].get_state() != start_facet_state)
|
if (tr_idx < 0 || visited[tr_idx] || m_triangles[tr_idx].get_state() != start_facet_state || is_facet_clipped(tr_idx, clp))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
assert(!m_triangles[tr_idx].is_split());
|
assert(!m_triangles[tr_idx].is_split());
|
||||||
@ -1687,11 +1695,12 @@ void TriangleSelector::seed_fill_apply_on_triangles(EnforcerBlockerType new_stat
|
|||||||
|
|
||||||
TriangleSelector::Cursor::Cursor(
|
TriangleSelector::Cursor::Cursor(
|
||||||
const Vec3f& center_, const Vec3f& source_, float radius_world,
|
const Vec3f& center_, const Vec3f& source_, float radius_world,
|
||||||
CursorType type_, const Transform3d& trafo_)
|
CursorType type_, const Transform3d& trafo_, const ClippingPlane &clipping_plane_)
|
||||||
: center{center_},
|
: center{center_},
|
||||||
source{source_},
|
source{source_},
|
||||||
type{type_},
|
type{type_},
|
||||||
trafo{trafo_.cast<float>()}
|
trafo{trafo_.cast<float>()},
|
||||||
|
clipping_plane(clipping_plane_)
|
||||||
{
|
{
|
||||||
Vec3d sf = Geometry::Transformation(trafo_).get_scaling_factor();
|
Vec3d sf = Geometry::Transformation(trafo_).get_scaling_factor();
|
||||||
if (is_approx(sf(0), sf(1)) && is_approx(sf(1), sf(2))) {
|
if (is_approx(sf(0), sf(1)) && is_approx(sf(1), sf(2))) {
|
||||||
@ -1714,22 +1723,19 @@ TriangleSelector::Cursor::Cursor(
|
|||||||
dir = (center - source).normalized();
|
dir = (center - source).normalized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Is a point (in mesh coords) inside a cursor?
|
// Is a point (in mesh coords) inside a cursor?
|
||||||
bool TriangleSelector::Cursor::is_mesh_point_inside(Vec3f point) const
|
bool TriangleSelector::Cursor::is_mesh_point_inside(const Vec3f &point) const
|
||||||
{
|
{
|
||||||
if (! uniform_scaling)
|
const Vec3f transformed_point = uniform_scaling ? point : Vec3f(trafo * point);
|
||||||
point = trafo * point;
|
const Vec3f diff = center - transformed_point;
|
||||||
|
const bool is_point_inside = (type == CIRCLE ? (diff - diff.dot(dir) * dir).squaredNorm() : diff.squaredNorm()) < radius_sqr;
|
||||||
|
|
||||||
Vec3f diff = center - point;
|
if (is_point_inside && clipping_plane.is_active())
|
||||||
return (type == CIRCLE ?
|
return !clipping_plane.is_mesh_point_clipped(point);
|
||||||
(diff - diff.dot(dir) * dir).squaredNorm() :
|
|
||||||
diff.squaredNorm())
|
return is_point_inside;
|
||||||
< radius_sqr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// p1, p2, p3 are in mesh coords!
|
// p1, p2, p3 are in mesh coords!
|
||||||
bool TriangleSelector::Cursor::is_pointer_in_triangle(const Vec3f& p1_,
|
bool TriangleSelector::Cursor::is_pointer_in_triangle(const Vec3f& p1_,
|
||||||
const Vec3f& p2_,
|
const Vec3f& p2_,
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
// #define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
|
// #define PRUSASLICER_TRIANGLE_SELECTOR_DEBUG
|
||||||
|
|
||||||
|
|
||||||
|
#include <cfloat>
|
||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
#include "TriangleMesh.hpp"
|
#include "TriangleMesh.hpp"
|
||||||
|
|
||||||
@ -22,6 +23,18 @@ public:
|
|||||||
POINTER
|
POINTER
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ClippingPlane
|
||||||
|
{
|
||||||
|
Vec3f normal;
|
||||||
|
float offset;
|
||||||
|
ClippingPlane() : normal{0.f, 0.f, 1.f}, offset{FLT_MAX} {};
|
||||||
|
explicit ClippingPlane(const std::array<float, 4> &clp) : normal{clp[0], clp[1], clp[2]}, offset{clp[3]} {}
|
||||||
|
|
||||||
|
bool is_active() const { return offset != FLT_MAX; }
|
||||||
|
|
||||||
|
bool is_mesh_point_clipped(const Vec3f &point) const { return normal.dot(point) - offset > 0.f; }
|
||||||
|
};
|
||||||
|
|
||||||
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> precompute_all_neighbors() const;
|
std::pair<std::vector<Vec3i>, std::vector<Vec3i>> precompute_all_neighbors() const;
|
||||||
void precompute_all_neighbors_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &neighbors_normal_out) const;
|
void precompute_all_neighbors_recursive(int facet_idx, const Vec3i &neighbors, const Vec3i &neighbors_propagated, std::vector<Vec3i> &neighbors_out, std::vector<Vec3i> &neighbors_normal_out) const;
|
||||||
|
|
||||||
@ -47,17 +60,20 @@ public:
|
|||||||
const Transform3d &trafo, // matrix to get from mesh to world
|
const Transform3d &trafo, // matrix to get from mesh to world
|
||||||
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
||||||
bool triangle_splitting, // If triangles will be split base on the cursor or not
|
bool triangle_splitting, // If triangles will be split base on the cursor or not
|
||||||
|
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
|
||||||
float highlight_by_angle_deg = 0.f); // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
|
float highlight_by_angle_deg = 0.f); // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
|
||||||
|
|
||||||
void seed_fill_select_triangles(const Vec3f &hit, // point where to start
|
void seed_fill_select_triangles(const Vec3f &hit, // point where to start
|
||||||
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||||
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
|
||||||
|
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
|
||||||
float seed_fill_angle, // the maximal angle between two facets to be painted by the same color
|
float seed_fill_angle, // the maximal angle between two facets to be painted by the same color
|
||||||
float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
|
float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
|
||||||
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
|
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
|
||||||
|
|
||||||
void bucket_fill_select_triangles(const Vec3f &hit, // point where to start
|
void bucket_fill_select_triangles(const Vec3f &hit, // point where to start
|
||||||
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
|
||||||
|
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
|
||||||
bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to.
|
bool propagate, // if bucket fill is propagated to neighbor faces or if it fills the only facet of the modified mesh that the hit point belongs to.
|
||||||
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
|
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
|
||||||
|
|
||||||
@ -183,8 +199,8 @@ protected:
|
|||||||
struct Cursor {
|
struct Cursor {
|
||||||
Cursor() = default;
|
Cursor() = default;
|
||||||
Cursor(const Vec3f& center_, const Vec3f& source_, float radius_world,
|
Cursor(const Vec3f& center_, const Vec3f& source_, float radius_world,
|
||||||
CursorType type_, const Transform3d& trafo_);
|
CursorType type_, const Transform3d& trafo_, const ClippingPlane &clipping_plane_);
|
||||||
bool is_mesh_point_inside(Vec3f pt) const;
|
bool is_mesh_point_inside(const Vec3f &pt) const;
|
||||||
bool is_pointer_in_triangle(const Vec3f& p1, const Vec3f& p2, const Vec3f& p3) const;
|
bool is_pointer_in_triangle(const Vec3f& p1, const Vec3f& p2, const Vec3f& p3) const;
|
||||||
|
|
||||||
Vec3f center;
|
Vec3f center;
|
||||||
@ -195,6 +211,7 @@ protected:
|
|||||||
Transform3f trafo;
|
Transform3f trafo;
|
||||||
Transform3f trafo_normal;
|
Transform3f trafo_normal;
|
||||||
bool uniform_scaling;
|
bool uniform_scaling;
|
||||||
|
ClippingPlane clipping_plane;
|
||||||
};
|
};
|
||||||
|
|
||||||
Cursor m_cursor;
|
Cursor m_cursor;
|
||||||
@ -211,6 +228,7 @@ private:
|
|||||||
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
|
void remove_useless_children(int facet_idx); // No hidden meaning. Triangles are meant.
|
||||||
bool is_pointer_in_triangle(int facet_idx) const;
|
bool is_pointer_in_triangle(int facet_idx) const;
|
||||||
bool is_edge_inside_cursor(int facet_idx) const;
|
bool is_edge_inside_cursor(int facet_idx) const;
|
||||||
|
bool is_facet_clipped(int facet_idx, const ClippingPlane &clp) const;
|
||||||
int push_triangle(int a, int b, int c, int source_triangle, EnforcerBlockerType state = EnforcerBlockerType{0});
|
int push_triangle(int a, int b, int c, int source_triangle, EnforcerBlockerType state = EnforcerBlockerType{0});
|
||||||
void perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state);
|
void perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state);
|
||||||
Vec3i child_neighbors(const Triangle &tr, const Vec3i &neighbors, int child_idx) const;
|
Vec3i child_neighbors(const Triangle &tr, const Vec3i &neighbors, int child_idx) const;
|
||||||
|
@ -2080,7 +2080,7 @@ void Control::auto_color_change()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt();
|
int extruders_cnt = GUI::wxGetApp().extruders_edited_cnt();
|
||||||
int extruder = 2;
|
// int extruder = 2;
|
||||||
|
|
||||||
const Print& print = GUI::wxGetApp().plater()->fff_print();
|
const Print& print = GUI::wxGetApp().plater()->fff_print();
|
||||||
for (auto object : print.objects()) {
|
for (auto object : print.objects()) {
|
||||||
|
@ -1448,7 +1448,7 @@ void ObjectList::load_subobject(ModelVolumeType type, bool from_galery/* = false
|
|||||||
selection_changed();
|
selection_changed();
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
void ObjectList::load_part(ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery/* = false* /)
|
void ObjectList::load_part(ModelObject& model_object, std::vector<ModelVolume*>& added_volumes, ModelVolumeType type, bool from_galery = false)
|
||||||
{
|
{
|
||||||
if (type != ModelVolumeType::MODEL_PART)
|
if (type != ModelVolumeType::MODEL_PART)
|
||||||
return;
|
return;
|
||||||
|
@ -267,7 +267,8 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||||||
const ModelObject *mo = m_c->selection_info()->model_object();
|
const ModelObject *mo = m_c->selection_info()->model_object();
|
||||||
const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
|
const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
|
||||||
const Transform3d trafo_matrix_not_translate = mi->get_transformation().get_matrix(true) * mo->volumes[m_rr.mesh_id]->get_matrix(true);
|
const Transform3d trafo_matrix_not_translate = mi->get_transformation().get_matrix(true) * mo->volumes[m_rr.mesh_id]->get_matrix(true);
|
||||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, m_smart_fill_angle,
|
const Transform3d trafo_matrix = mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix();
|
||||||
|
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, this->get_clipping_plane_in_volume_coordinates(trafo_matrix), m_smart_fill_angle,
|
||||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true);
|
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true);
|
||||||
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
||||||
m_seed_fill_last_mesh_id = m_rr.mesh_id;
|
m_seed_fill_last_mesh_id = m_rr.mesh_id;
|
||||||
@ -364,22 +365,22 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||||||
Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
|
Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
|
||||||
|
|
||||||
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
||||||
|
const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
|
||||||
if (m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) {
|
if (m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) {
|
||||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state);
|
m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state);
|
||||||
if (m_tool_type == ToolType::SMART_FILL)
|
if (m_tool_type == ToolType::SMART_FILL)
|
||||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, m_smart_fill_angle,
|
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle,
|
||||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true);
|
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true);
|
||||||
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
|
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
|
||||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), false, true);
|
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, false, true);
|
||||||
else if (m_tool_type == ToolType::BUCKET_FILL)
|
else if (m_tool_type == ToolType::BUCKET_FILL)
|
||||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), true, true);
|
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, true, true);
|
||||||
|
|
||||||
m_seed_fill_last_mesh_id = -1;
|
m_seed_fill_last_mesh_id = -1;
|
||||||
} else if (m_tool_type == ToolType::BRUSH)
|
} else if (m_tool_type == ToolType::BRUSH)
|
||||||
m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type,
|
m_triangle_selectors[m_rr.mesh_id]->select_patch(m_rr.hit, int(m_rr.facet), camera_pos, m_cursor_radius, m_cursor_type,
|
||||||
new_state, trafo_matrix, trafo_matrix_not_translate, m_triangle_splitting_enabled,
|
new_state, trafo_matrix, trafo_matrix_not_translate, m_triangle_splitting_enabled, clp,
|
||||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
|
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
|
||||||
|
|
||||||
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
||||||
m_last_mouse_click = mouse_position;
|
m_last_mouse_click = mouse_position;
|
||||||
}
|
}
|
||||||
@ -430,16 +431,18 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||||||
if(m_rr.mesh_id != m_seed_fill_last_mesh_id)
|
if(m_rr.mesh_id != m_seed_fill_last_mesh_id)
|
||||||
seed_fill_unselect_all();
|
seed_fill_unselect_all();
|
||||||
|
|
||||||
|
const Transform3d &trafo_matrix = trafo_matrices[m_rr.mesh_id];
|
||||||
const Transform3d &trafo_matrix_not_translate = trafo_matrices_not_translate[m_rr.mesh_id];
|
const Transform3d &trafo_matrix_not_translate = trafo_matrices_not_translate[m_rr.mesh_id];
|
||||||
|
|
||||||
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
||||||
|
const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
|
||||||
if (m_tool_type == ToolType::SMART_FILL)
|
if (m_tool_type == ToolType::SMART_FILL)
|
||||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, m_smart_fill_angle,
|
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle,
|
||||||
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
|
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
|
||||||
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
|
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
|
||||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), false);
|
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, false);
|
||||||
else if (m_tool_type == ToolType::BUCKET_FILL)
|
else if (m_tool_type == ToolType::BUCKET_FILL)
|
||||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), true);
|
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, true);
|
||||||
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
||||||
m_seed_fill_last_mesh_id = m_rr.mesh_id;
|
m_seed_fill_last_mesh_id = m_rr.mesh_id;
|
||||||
return true;
|
return true;
|
||||||
@ -569,6 +572,25 @@ void GLGizmoPainterBase::on_load(cereal::BinaryInputArchive&)
|
|||||||
m_schedule_update = true;
|
m_schedule_update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TriangleSelector::ClippingPlane GLGizmoPainterBase::get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const {
|
||||||
|
const ::Slic3r::GUI::ClippingPlane *const clipping_plane = m_c->object_clipper()->get_clipping_plane();
|
||||||
|
if (clipping_plane == nullptr || !clipping_plane->is_active())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const Vec3d clp_normal = clipping_plane->get_normal();
|
||||||
|
const double clp_offset = clipping_plane->get_offset();
|
||||||
|
|
||||||
|
const Transform3d trafo_normal = Transform3d(trafo.linear().transpose());
|
||||||
|
const Transform3d trafo_inv = trafo.inverse();
|
||||||
|
|
||||||
|
Vec3d point_on_plane = clp_normal * clp_offset;
|
||||||
|
Vec3d point_on_plane_transformed = trafo_inv * point_on_plane;
|
||||||
|
Vec3d normal_transformed = trafo_normal * clp_normal;
|
||||||
|
auto offset_transformed = float(point_on_plane_transformed.dot(normal_transformed));
|
||||||
|
|
||||||
|
return TriangleSelector::ClippingPlane({float(normal_transformed.x()), float(normal_transformed.y()), float(normal_transformed.z()), offset_transformed});
|
||||||
|
}
|
||||||
|
|
||||||
std::array<float, 4> TriangleSelectorGUI::get_seed_fill_color(const std::array<float, 4> &base_color)
|
std::array<float, 4> TriangleSelectorGUI::get_seed_fill_color(const std::array<float, 4> &base_color)
|
||||||
{
|
{
|
||||||
return {base_color[0] * 0.75f, base_color[1] * 0.75f, base_color[2] * 0.75f, 1.f};
|
return {base_color[0] * 0.75f, base_color[1] * 0.75f, base_color[2] * 0.75f, 1.f};
|
||||||
|
@ -184,6 +184,8 @@ protected:
|
|||||||
|
|
||||||
ClippingPlaneDataWrapper get_clipping_plane_data() const;
|
ClippingPlaneDataWrapper get_clipping_plane_data() const;
|
||||||
|
|
||||||
|
TriangleSelector::ClippingPlane get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const;
|
bool is_mesh_point_clipped(const Vec3d& point, const Transform3d& trafo) const;
|
||||||
void update_raycast_cache(const Vec2d& mouse_position,
|
void update_raycast_cache(const Vec2d& mouse_position,
|
||||||
|
@ -265,7 +265,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||||||
|
|
||||||
ImGui::NewLine();
|
ImGui::NewLine();
|
||||||
ImGui::SameLine(m_gui_cfg->bottom_left_width);
|
ImGui::SameLine(m_gui_cfg->bottom_left_width);
|
||||||
ImGui::Text(_L("%d triangles").c_str(), m_configuration.wanted_count);
|
ImGui::Text(_u8L("%d triangles").c_str(), m_configuration.wanted_count);
|
||||||
m_imgui->disabled_end(); // use_count
|
m_imgui->disabled_end(); // use_count
|
||||||
|
|
||||||
if (ImGui::Checkbox(_u8L("Show wireframe").c_str(), &m_show_wireframe)) {
|
if (ImGui::Checkbox(_u8L("Show wireframe").c_str(), &m_show_wireframe)) {
|
||||||
@ -275,7 +275,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||||||
|
|
||||||
bool is_canceling = m_state == State::canceling;
|
bool is_canceling = m_state == State::canceling;
|
||||||
m_imgui->disabled_begin(is_canceling);
|
m_imgui->disabled_begin(is_canceling);
|
||||||
if (m_imgui->button(_L("Cancel"))) {
|
if (m_imgui->button(_u8L("Cancel"))) {
|
||||||
if (m_state == State::settings) {
|
if (m_state == State::settings) {
|
||||||
if (m_original_its.has_value()) {
|
if (m_original_its.has_value()) {
|
||||||
set_its(*m_original_its);
|
set_its(*m_original_its);
|
||||||
@ -294,7 +294,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||||||
|
|
||||||
bool is_processing = m_state != State::settings;
|
bool is_processing = m_state != State::settings;
|
||||||
m_imgui->disabled_begin(is_processing);
|
m_imgui->disabled_begin(is_processing);
|
||||||
if (m_imgui->button(_L("Apply"))) {
|
if (m_imgui->button(_u8L("Apply"))) {
|
||||||
if (!m_is_valid_result) {
|
if (!m_is_valid_result) {
|
||||||
m_state = State::close_on_end;
|
m_state = State::close_on_end;
|
||||||
process();
|
process();
|
||||||
@ -395,7 +395,7 @@ void GLGizmoSimplify::process()
|
|||||||
|
|
||||||
// store previous state
|
// store previous state
|
||||||
auto plater = wxGetApp().plater();
|
auto plater = wxGetApp().plater();
|
||||||
plater->take_snapshot(_L("Simplify ") + m_volume->name);
|
plater->take_snapshot(_u8L("Simplify ") + m_volume->name);
|
||||||
plater->clear_before_change_mesh(m_obj_index);
|
plater->clear_before_change_mesh(m_obj_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -717,6 +717,10 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
|
|||||||
}
|
}
|
||||||
else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) {
|
else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) {
|
||||||
// to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active
|
// to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active
|
||||||
|
#if ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||||
|
selection.stop_dragging();
|
||||||
|
wxGetApp().obj_manipul()->set_dirty();
|
||||||
|
#endif // ENABLE_OUT_OF_BED_DETECTION_IMPROVEMENTS
|
||||||
processed = true;
|
processed = true;
|
||||||
}
|
}
|
||||||
else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) {
|
else if (evt.RightUp() && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && !m_parent.is_mouse_dragging()) {
|
||||||
|
@ -306,7 +306,7 @@ ImVec2 ImGuiWrapper::get_item_spacing() const
|
|||||||
{
|
{
|
||||||
const ImGuiContext &g = *GImGui;
|
const ImGuiContext &g = *GImGui;
|
||||||
const ImGuiStyle &style = g.Style;
|
const ImGuiStyle &style = g.Style;
|
||||||
return g.Style.ItemSpacing;
|
return style.ItemSpacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ImGuiWrapper::get_slider_float_height() const
|
float ImGuiWrapper::get_slider_float_height() const
|
||||||
|
@ -53,6 +53,7 @@ public:
|
|||||||
m_data[2] = norm_dir.z();
|
m_data[2] = norm_dir.z();
|
||||||
}
|
}
|
||||||
void set_offset(double offset) { m_data[3] = offset; }
|
void set_offset(double offset) { m_data[3] = offset; }
|
||||||
|
double get_offset() const { return m_data[3]; }
|
||||||
Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
|
Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
|
||||||
bool is_active() const { return m_data[3] != DBL_MAX; }
|
bool is_active() const { return m_data[3] != DBL_MAX; }
|
||||||
static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
|
static ClippingPlane ClipsNothing() { return ClippingPlane(Vec3d(0., 0., 1.), DBL_MAX); }
|
||||||
|
@ -205,7 +205,7 @@ void ObjectInfo::msw_rescale()
|
|||||||
|
|
||||||
void ObjectInfo::update_warning_icon(const std::string& warning_icon_name)
|
void ObjectInfo::update_warning_icon(const std::string& warning_icon_name)
|
||||||
{
|
{
|
||||||
if (showing_manifold_warning_icon = !warning_icon_name.empty()) {
|
if ((showing_manifold_warning_icon = !warning_icon_name.empty())) {
|
||||||
m_warning_icon_name = warning_icon_name;
|
m_warning_icon_name = warning_icon_name;
|
||||||
manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name));
|
manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name));
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ SCENARIO("DynamicPrintConfig serialization", "[Config]") {
|
|||||||
cereal::BinaryOutputArchive oarchive(ss);
|
cereal::BinaryOutputArchive oarchive(ss);
|
||||||
oarchive(cfg);
|
oarchive(cfg);
|
||||||
serialized = ss.str();
|
serialized = ss.str();
|
||||||
} catch (std::runtime_error e) {
|
} catch (std::runtime_error &e) {
|
||||||
e.what();
|
e.what();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +230,7 @@ SCENARIO("DynamicPrintConfig serialization", "[Config]") {
|
|||||||
std::stringstream ss(serialized);
|
std::stringstream ss(serialized);
|
||||||
cereal::BinaryInputArchive iarchive(ss);
|
cereal::BinaryInputArchive iarchive(ss);
|
||||||
iarchive(cfg2);
|
iarchive(cfg2);
|
||||||
} catch (std::runtime_error e) {
|
} catch (std::runtime_error &e) {
|
||||||
e.what();
|
e.what();
|
||||||
}
|
}
|
||||||
REQUIRE(cfg == cfg2);
|
REQUIRE(cfg == cfg2);
|
||||||
|
Loading…
Reference in New Issue
Block a user