Sync most of the gizmos with latest PrusaSlicer

This commit is contained in:
enricoturri1966 2023-10-30 23:10:05 +08:00 committed by Noisyfox
parent 049dfd3e08
commit 1561d65712
33 changed files with 843 additions and 760 deletions

View file

@ -1,3 +1,13 @@
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, David Kocík @kocikdav, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral
///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n
///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill
///|/ Copyright (c) 2020 Benjamin Greiner
///|/ Copyright (c) 2019 John Drake @foxox
///|/ Copyright (c) 2019 BeldrothTheGold @BeldrothTheGold
///|/ Copyright (c) 2019 Thomas Moore
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/libslic3r.h"
#include "GLCanvas3D.hpp"
@ -4505,12 +4515,13 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
for (int i = 0; i < static_cast<int>(m_model->objects.size()); ++i) {
const ModelObject* obj = m_model->objects[i];
for (int j = 0; j < static_cast<int>(obj->instances.size()); ++j) {
if (snapshot_type.empty() && m_selection.get_object_idx() == i) {
if (snapshot_type == L("Gizmo-Place on Face") && m_selection.get_object_idx() == i) {
// This means we are flattening this object. In that case pretend
// that it is not sinking (even if it is), so it is placed on bed
// later on (whatever is sinking will be left sinking).
min_zs[{ i, j }] = SINKING_Z_THRESHOLD;
} else
}
else
min_zs[{ i, j }] = obj->instance_bounding_box(j).min.z();
}
@ -5994,21 +6005,12 @@ bool GLCanvas3D::_init_main_toolbar()
return true;
}
// init arrow
BackgroundTexture::Metadata arrow_data;
arrow_data.filename = "toolbar_arrow.svg";
arrow_data.left = 0;
arrow_data.top = 0;
arrow_data.right = 0;
arrow_data.bottom = 0;
if (!m_main_toolbar.init_arrow(arrow_data))
{
if (!m_main_toolbar.init_arrow("toolbar_arrow.svg"))
BOOST_LOG_TRIVIAL(error) << "Main toolbar failed to load arrow texture.";
}
// m_gizmos is created at constructor, thus we can init arrow here.
if (!m_gizmos.init_arrow(arrow_data))
{
if (!m_gizmos.init_arrow("toolbar_arrow.svg"))
BOOST_LOG_TRIVIAL(error) << "Gizmos manager failed to load arrow texture.";
}
m_main_toolbar.set_layout_type(GLToolbar::Layout::Horizontal);
//BBS: main toolbar is at the top and left, we don't need the rounded-corner effect at the right side and the top side
@ -6393,7 +6395,7 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
void GLCanvas3D::_picking_pass()
{
if (!m_picking_enabled || m_mouse.dragging || m_mouse.position == Vec2d(DBL_MAX, DBL_MAX) && !m_gizmos.is_dragging()) {
if (!m_picking_enabled || m_mouse.dragging || m_mouse.position == Vec2d(DBL_MAX, DBL_MAX) || m_gizmos.is_dragging()) {
#if ENABLE_RAYCAST_PICKING_DEBUG
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.begin(std::string("Hit result"), ImGuiWindowFlags_AlwaysAutoResize);
@ -6421,12 +6423,11 @@ void GLCanvas3D::_picking_pass()
const GLVolume* volume = m_volumes.volumes[hit.raycaster_id];
if (volume->is_active && !volume->disabled && (volume->composite_id.volume_id >= 0 || m_render_sla_auxiliaries)) {
// do not add the volume id if any gizmo is active and CTRL is pressed
if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL)) {
if (m_gizmos.get_current_type() == GLGizmosManager::EType::Undefined || !wxGetKeyState(WXK_CONTROL))
m_hover_volume_idxs.emplace_back(hit.raycaster_id);
m_gizmos.set_hover_id(-1);
}
}
}
else
assert(false);
@ -6435,7 +6436,7 @@ void GLCanvas3D::_picking_pass()
case SceneRaycaster::EType::Gizmo:
{
const Size& cnv_size = get_canvas_size();
bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() &&
const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() &&
0 <= m_mouse.position.y() && m_mouse.position.y() < cnv_size.get_height();
m_gizmos.set_hover_id(inside ? hit.raycaster_id : -1);
break;

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2022 Enrico Turri @enricoturri1966, David Kocík @kocikdav, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/Point.hpp"
#include "libslic3r/libslic3r.h"
@ -281,21 +285,13 @@ bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
return res;
}
bool GLToolbar::init_arrow(const BackgroundTexture::Metadata& arrow_texture)
bool GLToolbar::init_arrow(const std::string& filename)
{
if (m_arrow_texture.texture.get_id() != 0)
if (m_arrow_texture.get_id() != 0)
return true;
std::string path = resources_dir() + "/images/";
bool res = false;
if (!arrow_texture.filename.empty()) {
res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000);
}
if (res)
m_arrow_texture.metadata = arrow_texture;
return res;
const std::string path = resources_dir() + "/images/";
return (!filename.empty()) ? m_arrow_texture.load_from_svg_file(path + filename, false, false, false, 1000) : false;
}
GLToolbar::Layout::EType GLToolbar::get_layout_type() const
@ -1300,7 +1296,7 @@ void GLToolbar::render_background(float left, float top, float right, float bott
void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item)
{
// arrow texture not initialized
if (m_arrow_texture.texture.get_id() == 0)
if (m_arrow_texture.get_id() == 0)
return;
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
@ -1339,27 +1335,24 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
top -= separator_stride;
float right = left + scaled_icons_size;
unsigned int tex_id = m_arrow_texture.texture.get_id();
const unsigned int tex_id = m_arrow_texture.get_id();
// arrow width and height
float arr_tex_width = (float)m_arrow_texture.texture.get_width();
float arr_tex_height = (float)m_arrow_texture.texture.get_height();
if ((tex_id != 0) && (arr_tex_width > 0) && (arr_tex_height > 0)) {
float inv_tex_width = (arr_tex_width != 0.0f) ? 1.0f / arr_tex_width : 0.0f;
float inv_tex_height = (arr_tex_height != 0.0f) ? 1.0f / arr_tex_height : 0.0f;
const float arr_tex_width = (float)m_arrow_texture.get_width();
const float arr_tex_height = (float)m_arrow_texture.get_height();
if (tex_id != 0 && arr_tex_width > 0.0f && arr_tex_height > 0.0f) {
float internal_left = left + border - scaled_icons_size * 1.5f; // add scaled_icons_size for huge arrow
float internal_right = right - border + scaled_icons_size * 1.5f;
float internal_top = top - border;
// bottom is not moving and should be calculated from arrow texture sides ratio
float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
float arrow_sides_ratio = (float)m_arrow_texture.get_height() / (float)m_arrow_texture.get_width();
float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio ;
float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width;
float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width;
float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height;
float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height;
const float left_uv = 0.0f;
const float right_uv = 1.0f;
const float top_uv = 1.0f;
const float bottom_uv = 0.0f;
GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } });
GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { left_uv, top_uv }, { right_uv, top_uv }, { right_uv, bottom_uv }, { left_uv, bottom_uv } });
}
}

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2018 - 2022 Enrico Turri @enricoturri1966, David Kocík @kocikdav, Oleksandra Iushchenko @YuSanka, Vojtěch Král @vojtechkral, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLToolbar_hpp_
#define slic3r_GLToolbar_hpp_
@ -327,7 +331,7 @@ private:
mutable GLTexture m_images_texture;
mutable bool m_images_texture_dirty;
BackgroundTexture m_background_texture;
BackgroundTexture m_arrow_texture;
GLTexture m_arrow_texture;
Layout m_layout;
ItemsList m_items;
@ -354,7 +358,7 @@ public:
bool init(const BackgroundTexture::Metadata& background_texture);
bool init_arrow(const BackgroundTexture::Metadata& arrow_texture);
bool init_arrow(const std::string& filename);
Layout::EType get_layout_type() const;
void set_layout_type(Layout::EType type);

View file

@ -17,6 +17,8 @@
#include <imgui/imgui_internal.h>
#include "slic3r/GUI/CameraUtils.hpp"
namespace Slic3r {
namespace GUI {
@ -111,9 +113,9 @@ GLGizmoAdvancedCut::GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& ic
m_buffered_rotation.setZero();
}
void GLGizmoAdvancedCut::data_changed()
void GLGizmoAdvancedCut::data_changed(bool is_serializing)
{
GLGizmoRotate3D::data_changed();
GLGizmoRotate3D::data_changed(is_serializing);
finish_rotation();
}
@ -227,7 +229,7 @@ bool GLGizmoAdvancedCut::unproject_on_cut_plane(const Vec2d &mouse_pos, Vec3d &p
Vec3d point;
Vec3d direction;
Vec3d hit;
MeshRaycaster::line_from_mouse_pos_static(mouse_pos, Transform3d::Identity(), camera, point, direction);
CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction);
Vec3d normal = -cp->get_normal().cast<double>();
double den = normal.dot(direction);
if (den != 0.) {
@ -1876,7 +1878,7 @@ bool GLGizmoAdvancedCut::process_cut_line(SLAGizmoEventType action, const Vec2d
Vec3d pt;
Vec3d dir;
MeshRaycaster::line_from_mouse_pos_static(mouse_position, Transform3d::Identity(), camera, pt, dir);
CameraUtils::ray_from_screen_pos(camera, mouse_position, pt, dir);
dir.normalize();
pt += dir; // Move the pt along dir so it is not clipped.

View file

@ -138,7 +138,7 @@ public:
virtual bool apply_clipping_plane() { return m_connectors_editing; }
void data_changed() override;
void data_changed(bool is_serializing) override;
protected:
bool on_init() override;

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoBase.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
@ -196,12 +200,21 @@ void GLGizmoBase::Grabber::render(float size, const ColorRGBA& render_color)
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: m_parent(parent)
, m_group_id(-1)
, m_state(Off)
, m_shortcut_key(NO_SHORTCUT_KEY_VALUE)
, m_icon_filename(icon_filename)
, m_sprite_id(sprite_id)
, m_imgui(wxGetApp().imgui())
{
}
std::string GLGizmoBase::get_action_snapshot_name() const
{
return "Gizmo action";
}
void GLGizmoBase::set_icon_filename(const std::string &filename) {
m_icon_filename = filename;
}
@ -212,8 +225,8 @@ void GLGizmoBase::set_hover_id(int id)
assert(!m_dragging);
// allow empty grabbers when not using grabbers but use hover_id - flatten, rotate
if (!m_grabbers.empty() && id >= (int) m_grabbers.size())
return;
// if (!m_grabbers.empty() && id >= (int) m_grabbers.size())
// return;
m_hover_id = id;
on_set_hover_id();
@ -266,15 +279,6 @@ void GLGizmoBase::unregister_grabbers_for_picking()
}
}
ColorRGBA GLGizmoBase::picking_color_component(unsigned int id) const
{
id = BASE_ID - id;
if (m_group_id > -1)
id -= m_group_id;
return picking_decode(id);
}
void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
{
#if ENABLE_FIXED_GRABBER
@ -285,16 +289,23 @@ void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
}
void GLGizmoBase::render_grabbers(float size) const
{
render_grabbers(0, m_grabbers.size() - 1, size, false);
}
void GLGizmoBase::render_grabbers(size_t first, size_t last, float size, bool force_hover) const
{
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader == nullptr)
return;
shader->start_using();
shader->set_uniform("emission_factor", 0.1f);
for (int i = 0; i < (int)m_grabbers.size(); ++i) {
glsafe(::glDisable(GL_CULL_FACE));
for (size_t i = first; i <= last; ++i) {
if (m_grabbers[i].enabled)
m_grabbers[i].render(m_hover_id == i, size);
m_grabbers[i].render(force_hover ? true : m_hover_id == (int)i, size);
}
glsafe(::glEnable(GL_CULL_FACE));
shader->stop_using();
}
@ -311,17 +322,15 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) {
if (mouse_event.LeftDown()) {
Selection &selection = m_parent.get_selection();
if (!selection.is_empty() && m_hover_id != -1 &&
(m_grabbers.empty() || m_hover_id < static_cast<int>(m_grabbers.size()))) {
if (!selection.is_empty() && m_hover_id != -1 /* &&
(m_grabbers.empty() || m_hover_id < static_cast<int>(m_grabbers.size()))*/) {
selection.setup_cache();
m_dragging = true;
for (auto &grabber : m_grabbers) grabber.dragging = false;
if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size()))
m_grabbers[m_hover_id].dragging = true;
// if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size()))
// m_grabbers[m_hover_id].dragging = true;
// prevent change of hover_id during dragging
m_parent.set_mouse_as_dragging();
on_start_dragging();
// Let the plater know that the dragging started
@ -333,7 +342,6 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) {
// when mouse cursor leave window than finish actual dragging operation
bool is_leaving = mouse_event.Leaving();
if (mouse_event.Dragging()) {
m_parent.set_mouse_as_dragging();
Point mouse_coord(mouse_event.GetX(), mouse_event.GetY());
auto ray = m_parent.mouse_ray(mouse_coord);
UpdateData data(ray, mouse_coord);
@ -343,13 +351,23 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) {
wxGetApp().obj_manipul()->set_dirty();
m_parent.set_as_dirty();
return true;
} else if (mouse_event.LeftUp() || is_leaving || is_dragging_finished) {
}
else if (mouse_event.LeftUp() || is_leaving || is_dragging_finished) {
do_stop_dragging(is_leaving);
return true;
}
}
return false;
}
void GLGizmoBase::do_stop_dragging(bool perform_mouse_cleanup)
{
for (auto& grabber : m_grabbers) grabber.dragging = false;
m_dragging = false;
// NOTE: This should be part of GLCanvas3D
// Reset hover_id when leave window
if (is_leaving) m_parent.mouse_up_cleanup();
if (perform_mouse_cleanup) m_parent.mouse_up_cleanup();
on_stop_dragging();
@ -366,10 +384,6 @@ bool GLGizmoBase::use_grabbers(const wxMouseEvent &mouse_event) {
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
// updates camera target constraints
m_parent.refresh_camera_scene_box();
return true;
}
}
return false;
}
std::string GLGizmoBase::format(float value, unsigned int decimals) const
@ -385,9 +399,12 @@ void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
{
on_render_input_window(x, y, bottom_limit);
if (m_first_input_window_render) {
// for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame
// so, we forces another frame rendering the first time the imgui window is shown
// imgui windows that don't have an initial size needs to be processed once to get one
// and are not rendered in the first frame
// so, we forces to render another frame the first time the imgui window is shown
// https://github.com/ocornut/imgui/issues/2949
m_parent.set_as_dirty();
m_parent.request_extra_frame();
m_first_input_window_render = false;
}
}

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoBase_hpp_
#define slic3r_GLGizmoBase_hpp_
@ -68,6 +72,9 @@ public:
NegZ = 1 << 5,
};
// Represents NO key(button on keyboard) value
static const int NO_SHORTCUT_KEY_VALUE = 0;
protected:
struct Grabber
{
@ -129,9 +136,9 @@ public:
protected:
GLCanvas3D& m_parent;
int m_group_id{ -1 }; // TODO: remove only for rotate
EState m_state{ Off };
int m_shortcut_key{ 0 };
int m_group_id; // TODO: remove only for rotate
EState m_state;
int m_shortcut_key;
std::string m_icon_filename;
unsigned int m_sprite_id;
int m_hover_id{ -1 };
@ -171,7 +178,7 @@ public:
virtual bool wants_enter_leave_snapshots() const { return false; }
virtual std::string get_gizmo_entering_text() const { assert(false); return ""; }
virtual std::string get_gizmo_leaving_text() const { assert(false); return ""; }
virtual std::string get_action_snapshot_name() { return "Gizmo action"; }
virtual std::string get_action_snapshot_name() const;
void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; }
virtual bool apply_clipping_plane() { return true; }
@ -202,7 +209,7 @@ public:
/// <summary>
/// Is called when data (Selection) is changed
/// </summary>
virtual void data_changed(){};
virtual void data_changed(bool is_serializing){};
/// <summary>
/// Implement when want to process mouse events in gizmo
@ -246,11 +253,9 @@ protected:
virtual void on_register_raycasters_for_picking() {}
virtual void on_unregister_raycasters_for_picking() {}
// Returns the picking color for the given id, based on the BASE_ID constant
// No check is made for clashing with other picking color (i.e. GLVolumes)
ColorRGBA picking_color_component(unsigned int id) const;
void render_grabbers(const BoundingBoxf3& box) const;
void render_grabbers(float size) const;
void render_grabbers(size_t first, size_t last, float size, bool force_hover) const;
std::string format(float value, unsigned int decimals) const;
@ -265,6 +270,9 @@ protected:
/// <param name="mouse_event">Keep information about mouse click</param>
/// <returns>same as on_mouse</returns>
bool use_grabbers(const wxMouseEvent &mouse_event);
void do_stop_dragging(bool perform_mouse_cleanup);
private:
// Flag for dirty visible state of Gizmo
// When True then need new rendering

View file

@ -430,7 +430,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this]() {
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
@ -444,7 +444,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_drag_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (b_bbl_slider_float || b_drag_input) m_c->object_clipper()->set_position(clp_dist, true);
if (b_bbl_slider_float || b_drag_input) m_c->object_clipper()->set_position_by_ratio(clp_dist, true);
}
ImGui::Separator();

View file

@ -40,7 +40,7 @@ protected:
std::string get_gizmo_entering_text() const override { return "Entering Paint-on supports"; }
std::string get_gizmo_leaving_text() const override { return "Leaving Paint-on supports"; }
std::string get_action_snapshot_name() override { return "Paint-on supports editing"; }
std::string get_action_snapshot_name() const override { return "Paint-on supports editing"; }
// BBS
wchar_t m_current_tool = 0;

View file

@ -1,4 +1,7 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoFlatten.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
@ -22,53 +25,39 @@ GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filen
bool GLGizmoFlatten::on_mouse(const wxMouseEvent &mouse_event)
{
if (mouse_event.Moving()) {
// only for sure
m_mouse_left_down = false;
return false;
}
if (mouse_event.LeftDown()) {
if (m_hover_id != -1) {
m_mouse_left_down = true;
Selection &selection = m_parent.get_selection();
if (selection.is_single_full_instance()) {
// Rotate the object so the normal points downward:
selection.flattening_rotate(m_planes[m_hover_id].normal);
m_parent.do_rotate(L("Gizmo-Place on Face"));
wxGetApp().obj_manipul()->set_dirty();
}
return true;
}
// fix: prevent restart gizmo when reselect object
// take responsibility for left up
if (m_parent.get_first_hover_volume_idx() >= 0) m_mouse_left_down = true;
} else if (mouse_event.LeftUp()) {
if (m_mouse_left_down) {
// responsible for mouse left up after selecting plane
m_mouse_left_down = false;
return true;
}
} else if (mouse_event.Leaving()) {
m_mouse_left_down = false;
}
else if (mouse_event.LeftUp())
return m_hover_id != -1;
return false;
}
void GLGizmoFlatten::data_changed()
void GLGizmoFlatten::data_changed(bool is_serializing)
{
const Selection & selection = m_parent.get_selection();
const ModelObject *model_object = nullptr;
int instance_id = -1;
if (selection.is_single_full_instance() ||
selection.is_from_single_object() ) {
model_object = selection.get_model()->objects[selection.get_object_idx()];
instance_id = selection.get_instance_idx();
}
set_flattening_data(model_object);
set_flattening_data(model_object, instance_id);
}
bool GLGizmoFlatten::on_init()
{
// BBS
m_shortcut_key = WXK_CONTROL_F;
return true;
}
@ -111,7 +100,7 @@ void GLGizmoFlatten::on_render()
if (selection.is_single_full_instance()) {
const Transform3d& inst_matrix = selection.get_first_volume()->get_instance_transformation().get_matrix();
const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d model_matrix = Geometry::assemble_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * inst_matrix;
const Transform3d model_matrix = Geometry::translation_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) * inst_matrix;
const Transform3d view_model_matrix = camera.get_view_matrix() * model_matrix;
shader->set_uniform("view_model_matrix", view_model_matrix);
@ -119,7 +108,6 @@ void GLGizmoFlatten::on_render()
if (this->is_plane_update_necessary())
update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i) {
m_planes_casters[i]->set_transform(model_matrix);
m_planes[i].vbo.model.set_color(i == m_hover_id ? GLGizmoBase::FLATTEN_HOVER_COLOR : GLGizmoBase::FLATTEN_COLOR);
m_planes[i].vbo.model.render();
}
@ -139,7 +127,7 @@ void GLGizmoFlatten::on_register_raycasters_for_picking()
if (!m_planes.empty()) {
const Selection& selection = m_parent.get_selection();
const Transform3d matrix = Geometry::assemble_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) *
const Transform3d matrix = Geometry::translation_transform(selection.get_first_volume()->get_sla_shift_z() * Vec3d::UnitZ()) *
selection.get_first_volume()->get_instance_transformation().get_matrix();
for (int i = 0; i < (int)m_planes.size(); ++i) {
@ -155,9 +143,9 @@ void GLGizmoFlatten::on_unregister_raycasters_for_picking()
m_planes_casters.clear();
}
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object, int instance_id)
{
if (model_object != m_old_model_object) {
if (model_object != m_old_model_object || instance_id != m_old_instance_id) {
m_planes.clear();
on_unregister_raycasters_for_picking();
}
@ -237,9 +225,7 @@ void GLGizmoFlatten::update_planes()
}
// Let's prepare transformation of the normal vector from mesh to instance coordinates.
Geometry::Transformation t(inst_matrix);
Vec3d scaling = t.get_scaling_factor();
t.set_scaling_factor(Vec3d(1./scaling(0), 1./scaling(1), 1./scaling(2)));
const Matrix3d normal_matrix = inst_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
// Now we'll go through all the polygons, transform the points into xy plane to process them:
for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) {
@ -247,7 +233,7 @@ void GLGizmoFlatten::update_planes()
const Vec3d& normal = m_planes[polygon_id].normal;
// transform the normal according to the instance matrix:
Vec3d normal_transformed = t.get_matrix() * normal;
const Vec3d normal_transformed = normal_matrix * normal;
// We are going to rotate about z and y to flatten the plane
Eigen::Quaterniond q;
@ -260,7 +246,7 @@ void GLGizmoFlatten::update_planes()
// And yes, it is a nasty thing to do. Whoever has time is free to refactor.
Vec3d bb_size = BoundingBoxf3(polygon).size();
float sf = std::min(1./bb_size(0), 1./bb_size(1));
Transform3d tr = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(sf, sf, 1.f));
Transform3d tr = Geometry::scale_transform({ sf, sf, 1.f });
polygon = transform(polygon, tr);
polygon = Slic3r::Geometry::convex_hull(polygon);
polygon = transform(polygon, tr.inverse());
@ -365,6 +351,7 @@ void GLGizmoFlatten::update_planes()
m_first_instance_scale = mo->instances.front()->get_scaling_factor();
m_first_instance_mirror = mo->instances.front()->get_mirror();
m_old_model_object = mo;
m_old_instance_id = m_c->selection_info()->get_active_instance();
// And finally create respective VBOs. The polygon is convex with
// the vertices in order, so triangulation is trivial.
@ -378,18 +365,24 @@ void GLGizmoFlatten::update_planes()
for (size_t i = 1; i < plane.vertices.size() - 1; ++i) {
its.indices.emplace_back(0, i, i + 1); // triangle fan
}
plane.vbo.model.init_from(its);
if (Geometry::Transformation(inst_matrix).is_left_handed()) {
// we need to swap face normals in case the object is mirrored
// for the raycaster to work properly
for (stl_triangle_vertex_indices& face : its.indices) {
if (its_face_normal(its, face).cast<double>().dot(plane.normal) < 0.0)
std::swap(face[1], face[2]);
}
}
plane.vbo.mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
// FIXME: vertices should really be local, they need not
// persist now when we use VBOs
plane.vertices.clear();
plane.vertices.shrink_to_fit();
// vertices are no more needed, clear memory
plane.vertices = std::vector<Vec3d>();
}
on_register_raycasters_for_picking();
}
bool GLGizmoFlatten::is_plane_update_necessary() const
{
const ModelObject* mo = m_c->selection_info()->model_object();

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoFlatten_hpp_
#define slic3r_GLGizmoFlatten_hpp_
@ -35,9 +39,8 @@ private:
std::vector<PlaneData> m_planes;
std::vector<std::shared_ptr<SceneRaycasterItem>> m_planes_casters;
bool m_mouse_left_down = false; // for detection left_up of this gizmo
const ModelObject* m_old_model_object = nullptr;
std::vector<const Transform3d*> instances_matrices;
int m_old_instance_id{ -1 };
void update_planes();
bool is_plane_update_necessary() const;
@ -45,7 +48,7 @@ private:
public:
GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
void set_flattening_data(const ModelObject* model_object);
void set_flattening_data(const ModelObject* model_object, int instance_id);
/// <summary>
/// Apply rotation on select plane
@ -54,7 +57,7 @@ public:
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void data_changed() override;
void data_changed(bool is_serializing) override;
protected:
bool on_init() override;
std::string on_get_name() const override;

View file

@ -1,3 +1,8 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Bubník @bubnikv
///|/ Copyright (c) 2021 Justin Schuh @jschuh
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoMmuSegmentation.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
@ -162,9 +167,9 @@ void GLGizmoMmuSegmentation::render_painter_gizmo()
glsafe(::glDisable(GL_BLEND));
}
void GLGizmoMmuSegmentation::data_changed()
void GLGizmoMmuSegmentation::data_changed(bool is_serializing)
{
GLGizmoPainterBase::data_changed();
GLGizmoPainterBase::data_changed(is_serializing);
if (m_state != On || wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptFFF || wxGetApp().extruders_edited_cnt() <= 1)
return;
@ -571,7 +576,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this]() {
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
@ -584,7 +589,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true); }
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); }
} else if (m_current_tool == ImGui::TriangleButtonIcon) {
m_cursor_type = TriangleSelector::CursorType::POINTER;
@ -597,7 +602,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this]() {
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
@ -610,7 +615,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true); }
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); }
} else if (m_current_tool == ImGui::FillButtonIcon) {
m_cursor_type = TriangleSelector::CursorType::POINTER;
@ -644,7 +649,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this]() {
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
@ -657,7 +662,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true);}
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true);}
} else if (m_current_tool == ImGui::HeightRangeIcon) {
m_tool_type = ToolType::BRUSH;
@ -680,7 +685,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this]() {
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
@ -693,7 +698,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true); }
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); }
}
else if (m_current_tool == ImGui::GapFillIcon) {
m_tool_type = ToolType::GAP_FILL;

View file

@ -70,7 +70,7 @@ public:
void render_painter_gizmo() override;
void data_changed() override;
void data_changed(bool is_serializing) override;
void render_triangles(const Selection& selection) const override;
@ -104,7 +104,7 @@ protected:
std::string get_gizmo_entering_text() const override { return "Entering color painting"; }
std::string get_gizmo_leaving_text() const override { return "Leaving color painting"; }
std::string get_action_snapshot_name() override { return "Color painting editing"; }
std::string get_action_snapshot_name() const override { return "Color painting editing"; }
// BBS
size_t m_selected_extruder_idx = 0;

View file

@ -1,4 +1,7 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
///|/ Copyright (c) Prusa Research 2019 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoMove.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
@ -47,10 +50,8 @@ bool GLGizmoMove3D::on_mouse(const wxMouseEvent &mouse_event) {
return use_grabbers(mouse_event);
}
void GLGizmoMove3D::data_changed() {
const Selection &selection = m_parent.get_selection();
bool is_wipe_tower = selection.is_wipe_tower();
m_grabbers[2].enabled = !is_wipe_tower;
void GLGizmoMove3D::data_changed(bool is_serializing) {
m_grabbers[2].enabled = !m_parent.get_selection().is_wipe_tower();
}
bool GLGizmoMove3D::on_init()
@ -219,17 +220,17 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const
{
double projection = 0.0;
Vec3d starting_vec = m_starting_drag_position - m_starting_box_center;
double len_starting_vec = starting_vec.norm();
const Vec3d starting_vec = m_starting_drag_position - m_starting_box_center;
const double len_starting_vec = starting_vec.norm();
if (len_starting_vec != 0.0) {
Vec3d mouse_dir = data.mouse_ray.unit_vector();
const Vec3d mouse_dir = data.mouse_ray.unit_vector();
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
const Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) * mouse_dir;
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - m_starting_drag_position;
const Vec3d inters_vec = inters - m_starting_drag_position;
// finds projection of the vector along the staring direction
projection = inters_vec.dot(starting_vec.normalized());

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoMove_hpp_
#define slic3r_GLGizmoMove_hpp_
@ -52,7 +56,7 @@ public:
/// <summary>
/// Detect reduction of move for wipetover on selection change
/// </summary>
void data_changed() override;
void data_changed(bool is_serializing) override;
protected:
bool on_init() override;
std::string on_get_name() const override;

View file

@ -1,4 +1,7 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01, Lukáš Hejl @hejllukas
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoPainterBase.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp"
@ -34,7 +37,7 @@ GLGizmoPainterBase::~GLGizmoPainterBase()
s_sphere.reset();
}
void GLGizmoPainterBase::data_changed()
void GLGizmoPainterBase::data_changed(bool is_serializing)
{
if (m_state != On)
return;
@ -120,13 +123,11 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const
shader->set_uniform("volume_world_matrix", trafo_matrix);
m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix);
if (is_left_handed)
glsafe(::glFrontFace(GL_CCW));
}
}
void GLGizmoPainterBase::render_cursor()
{
// First check that the mouse pointer is on an object.
@ -165,8 +166,6 @@ void GLGizmoPainterBase::render_cursor()
}
}
void GLGizmoPainterBase::render_cursor_circle()
{
const Size cnv_size = m_parent.get_canvas_size();
@ -202,7 +201,7 @@ void GLGizmoPainterBase::render_cursor_circle()
init_data.reserve_indices(StepsCount);
// vertices + indices
for (unsigned short i = 0; i < StepsCount; ++i) {
for (unsigned int i = 0; i < StepsCount; ++i) {
const float angle = float(i * StepSize);
init_data.add_vertex(Vec2f(2.0f * ((center.x() + ::cos(angle) * radius) * cnv_inv_width - 0.5f),
-2.0f * ((center.y() + ::sin(angle) * radius) * cnv_inv_height - 0.5f)));
@ -221,7 +220,7 @@ void GLGizmoPainterBase::render_cursor_circle()
m_circle.set_color(render_color);
GLShaderProgram* shader = wxGetApp().get_shader("flat");
GLShaderProgram* shader = GUI::wxGetApp().get_shader("flat");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("view_model_matrix", Transform3d::Identity());
@ -247,10 +246,6 @@ void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const
return;
const Transform3d complete_scaling_matrix_inverse = Geometry::Transformation(trafo).get_matrix(true, true, false, true).inverse();
const bool is_left_handed = Geometry::Transformation(trafo).is_left_handed();
if (is_left_handed)
glFrontFace(GL_CW);
// BBS
ColorRGBA render_color = this->get_cursor_hover_color();
@ -258,6 +253,7 @@ void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const
render_color = this->get_cursor_sphere_left_button_color();
else if (m_button_down == Button::Right)
render_color = this->get_cursor_sphere_right_button_color();
shader->start_using();
const Camera& camera = wxGetApp().plater()->get_camera();
@ -268,13 +264,18 @@ void GLGizmoPainterBase::render_cursor_sphere(const Transform3d& trafo) const
shader->set_uniform("view_model_matrix", view_model_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
const bool is_left_handed = Geometry::Transformation(view_model_matrix).is_left_handed();
if (is_left_handed)
glsafe(::glFrontFace(GL_CW));
assert(s_sphere != nullptr);
s_sphere->set_color(render_color);
s_sphere->render();
shader->stop_using();
if (is_left_handed)
glFrontFace(GL_CCW);
glsafe(::glFrontFace(GL_CCW));
shader->stop_using();
}
// BBS
@ -631,13 +632,13 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
pos = action == SLAGizmoEventType::MouseWheelDown
? std::max(0., pos - 0.01)
: std::min(1., pos + 0.01);
m_c->object_clipper()->set_position(pos, true);
m_c->object_clipper()->set_position_by_ratio(pos, true);
return true;
}
}
if (action == SLAGizmoEventType::ResetClippingPlane) {
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
return true;
}
@ -794,8 +795,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
m_cursor_type, trafo_matrix, clp);
m_triangle_selectors[mesh_idx]->select_patch(int(first_position.facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate,
m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f);
}
else {
} else {
for (auto first_position_it = projected_mouse_positions.cbegin(); first_position_it != projected_mouse_positions.cend() - 1; ++first_position_it) {
auto second_position_it = first_position_it + 1;
std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::DoublePointCursor::cursor_factory(first_position_it->mesh_hit, second_position_it->mesh_hit, camera_pos, m_cursor_radius, m_cursor_type, trafo_matrix, clp);
@ -1190,10 +1190,6 @@ void TriangleSelectorGUI::update_render_data()
static const float offset = 0.001f;
for (const Triangle &tr : m_triangles) {
bool is_valid = tr.valid();
bool is_split = tr.is_split();
EnforcerBlockerType type = tr.get_state();
bool is_select_by_seed_fill = tr.is_selected_by_seed_fill();
if (!tr.valid() || tr.is_split() || (tr.get_state() == EnforcerBlockerType::NONE && !tr.is_selected_by_seed_fill()))
continue;

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Pavel Mikuš @Godrak, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, Filip Sykala @Jony01
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoPainterBase_hpp_
#define slic3r_GLGizmoPainterBase_hpp_
@ -186,10 +190,11 @@ private:
ObjectID m_old_mo_id;
size_t m_old_volumes_size = 0;
void on_render() override {}
public:
GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
virtual ~GLGizmoPainterBase() override;
void data_changed() override;
~GLGizmoPainterBase() override;
void data_changed(bool is_serializing) override;
virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
// Following function renders the triangles and cursor. Having this separated
@ -227,8 +232,8 @@ protected:
virtual void update_model_object() = 0;
virtual void update_from_model_object(bool first_update) = 0;
virtual ColorRGBA get_cursor_sphere_left_button_color() const { return {0.f, 0.f, 1.f, 0.25f}; }
virtual ColorRGBA get_cursor_sphere_right_button_color() const { return {1.f, 0.f, 0.f, 0.25f}; }
virtual ColorRGBA get_cursor_sphere_left_button_color() const { return { 0.0f, 0.0f, 1.0f, 0.25f }; }
virtual ColorRGBA get_cursor_sphere_right_button_color() const { return { 1.0f, 0.0f, 0.0f, 0.25f }; }
// BBS
virtual ColorRGBA get_cursor_hover_color() const { return { 0.f, 0.f, 0.f, 0.25f }; }
@ -339,7 +344,7 @@ private:
Vec3f hit;
size_t facet;
};
mutable RaycastResult m_rr;
mutable RaycastResult m_rr = {Vec2d::Zero(), -1, Vec3f::Zero(), 0};
// BBS
struct CutContours

View file

@ -1,4 +1,7 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoRotate.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/ImGuiWrapper.hpp"
@ -235,7 +238,7 @@ void GLGizmoRotate::render_circle(const ColorRGBA& color, bool radius_changed)
init_data.reserve_indices(ScaleStepsCount);
// vertices + indices
for (unsigned short i = 0; i < ScaleStepsCount; ++i) {
for (unsigned int i = 0; i < ScaleStepsCount; ++i) {
const float angle = float(i * ScaleStepRad);
init_data.add_vertex(Vec3f(::cos(angle) * m_radius, ::sin(angle) * m_radius, 0.0f));
init_data.add_index(i);
@ -262,7 +265,7 @@ void GLGizmoRotate::render_scale(const ColorRGBA& color, bool radius_changed)
init_data.reserve_indices(2 * ScaleStepsCount);
// vertices + indices
for (unsigned short i = 0; i < ScaleStepsCount; ++i) {
for (unsigned int i = 0; i < ScaleStepsCount; ++i) {
const float angle = float(i * ScaleStepRad);
const float cosa = ::cos(angle);
const float sina = ::sin(angle);
@ -271,10 +274,12 @@ void GLGizmoRotate::render_scale(const ColorRGBA& color, bool radius_changed)
const float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short;
const float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short;
// vertices
init_data.add_vertex(Vec3f(in_x, in_y, 0.0f));
init_data.add_vertex(Vec3f(out_x, out_y, 0.0f));
init_data.add_index(i * 2);
init_data.add_index(i * 2 + 1);
// indices
init_data.add_line(i * 2, i * 2 + 1);
}
m_scale.init_from(std::move(init_data));
@ -299,7 +304,7 @@ void GLGizmoRotate::render_snap_radii(const ColorRGBA& color, bool radius_change
init_data.reserve_indices(2 * ScaleStepsCount);
// vertices + indices
for (unsigned short i = 0; i < ScaleStepsCount; ++i) {
for (unsigned int i = 0; i < ScaleStepsCount; ++i) {
const float angle = float(i * step);
const float cosa = ::cos(angle);
const float sina = ::sin(angle);
@ -308,10 +313,12 @@ void GLGizmoRotate::render_snap_radii(const ColorRGBA& color, bool radius_change
const float out_x = cosa * out_radius;
const float out_y = sina * out_radius;
// vertices
init_data.add_vertex(Vec3f(in_x, in_y, 0.0f));
init_data.add_vertex(Vec3f(out_x, out_y, 0.0f));
init_data.add_index(i * 2);
init_data.add_index(i * 2 + 1);
// indices
init_data.add_line(i * 2, i * 2 + 1);
}
m_snap_radii.init_from(std::move(init_data));
@ -362,7 +369,7 @@ void GLGizmoRotate::render_angle_arc(const ColorRGBA& color, bool radius_changed
init_data.reserve_indices(1 + AngleResolution);
// vertices + indices
for (unsigned short i = 0; i <= AngleResolution; ++i) {
for (unsigned int i = 0; i <= AngleResolution; ++i) {
const float angle = float(i) * step_angle;
init_data.add_vertex(Vec3f(::cos(angle) * ex_radius, ::sin(angle) * ex_radius, 0.0f));
init_data.add_index(i);
@ -439,7 +446,7 @@ Transform3d GLGizmoRotate::local_transform(const Selection& selection) const
Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const
{
double half_pi = 0.5 * double(PI);
const double half_pi = 0.5 * double(PI);
Transform3d m = Transform3d::Identity();
@ -470,7 +477,20 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons
m.translate(-m_center);
return transform(mouse_ray, m).intersect_plane(0.0);
const Linef3 local_mouse_ray = transform(mouse_ray, m);
if (std::abs(local_mouse_ray.vector().dot(Vec3d::UnitZ())) < EPSILON) {
// if the ray is parallel to the plane containing the circle
if (std::abs(local_mouse_ray.vector().dot(Vec3d::UnitY())) > 1.0 - EPSILON)
// if the ray is parallel to grabber direction
return Vec3d::UnitX();
else {
const Vec3d world_pos = (local_mouse_ray.a.x() >= 0.0) ? mouse_ray.a - m_center : mouse_ray.b - m_center;
m.translate(m_center);
return m * world_pos;
}
}
else
return local_mouse_ray.intersect_plane(0.0);
}
//BBS: GUI refactor: add obj manipulation
@ -489,13 +509,14 @@ bool GLGizmoRotate3D::on_mouse(const wxMouseEvent &mouse_event) {
// Apply new temporary rotations
TransformationType transformation_type(
TransformationType::World_Relative_Joint);
if (mouse_event.AltDown()) transformation_type.set_independent();
if (mouse_event.AltDown())
transformation_type.set_independent();
m_parent.get_selection().rotate(get_rotation(), transformation_type);
}
return use_grabbers(mouse_event);
}
void GLGizmoRotate3D::data_changed() {
void GLGizmoRotate3D::data_changed(bool is_serializing) {
const Selection &selection = m_parent.get_selection();
bool is_wipe_tower = selection.is_wipe_tower();
if (is_wipe_tower) {

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, Tomáš Mészáros @tamasmeszaros
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoRotate_hpp_
#define slic3r_GLGizmoRotate_hpp_
@ -85,6 +89,7 @@ public:
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void dragging(const UpdateData &data);
protected:
bool on_init() override;
std::string on_get_name() const override { return ""; }
@ -122,7 +127,7 @@ public:
GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation);
Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); }
void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); }
void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation.x()); m_gizmos[Y].set_angle(rotation.y()); m_gizmos[Z].set_angle(rotation.z()); }
std::string get_tooltip() const override {
std::string tooltip = m_gizmos[X].get_tooltip();
@ -147,7 +152,7 @@ public:
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void data_changed() override;
void data_changed(bool is_serializing) override;
protected:
bool on_init() override;
std::string on_get_name() const override;

View file

@ -1,4 +1,7 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoScale.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
@ -83,18 +86,18 @@ bool GLGizmoScale3D::on_mouse(const wxMouseEvent &mouse_event)
if (m_dragging) {
// Apply new temporary scale factors
TransformationType transformation_type(TransformationType::Local_Absolute_Joint);
if (mouse_event.AltDown()) transformation_type.set_independent();
if (mouse_event.AltDown())
transformation_type.set_independent();
Selection& selection = m_parent.get_selection();
selection.scale(get_scale(), transformation_type);
selection.scale(m_scale, transformation_type);
if (mouse_event.CmdDown()) selection.translate(m_offset, true);
}
}
return use_grabbers(mouse_event);
}
void GLGizmoScale3D::data_changed()
{
void GLGizmoScale3D::data_changed(bool is_serializing) {
const Selection &selection = m_parent.get_selection();
bool enable_scale_xyz = selection.is_single_full_instance() ||
selection.is_single_volume() ||
@ -298,7 +301,13 @@ void GLGizmoScale3D::on_render()
}
// draw grabbers
shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("emission_factor", 0.1f);
render_grabbers(grabber_mean_size);
shader->stop_using();
}
}
void GLGizmoScale3D::on_register_raycasters_for_picking()
@ -390,7 +399,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
void GLGizmoScale3D::do_scale_uniform(const UpdateData & data)
{
double ratio = calc_ratio(data);
const double ratio = calc_ratio(data);
if (ratio > 0.0) {
m_scale = m_starting.scale * ratio;
m_offset = Vec3d::Zero();

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Filip Sykala @Jony01
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoScale_hpp_
#define slic3r_GLGizmoScale_hpp_
@ -28,10 +32,10 @@ class GLGizmoScale3D : public GLGizmoBase
StartingData() : scale(Vec3d::Ones()), drag_position(Vec3d::Zero()), ctrl_down(false) { for (int i = 0; i < 5; ++i) { pivots[i] = Vec3d::Zero(); } }
};
mutable BoundingBoxf3 m_box;
mutable Transform3d m_transform;
BoundingBoxf3 m_box;
Transform3d m_transform;
// Transforms grabbers offsets to the proper reference system (world for instances, instance for volumes)
mutable Transform3d m_offsets_transform;
Transform3d m_offsets_transform;
Vec3d m_scale{ Vec3d::Ones() };
Vec3d m_offset{ Vec3d::Zero() };
double m_snap_step{ 0.05 };
@ -66,8 +70,6 @@ public:
std::string get_tooltip() const override;
void enable_ununiversal_scale(bool enable);
/// <summary>
/// Postpone to Grabber for scale
/// </summary>
@ -75,7 +77,8 @@ public:
/// <returns>Return True when use the information otherwise False.</returns>
bool on_mouse(const wxMouseEvent &mouse_event) override;
void data_changed() override;
void data_changed(bool is_serializing) override;
void enable_ununiversal_scale(bool enable);
protected:
virtual bool on_init() override;
virtual std::string on_get_name() const override;

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2020 - 2022 Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoSeam.hpp"
#include "libslic3r/Model.hpp"
@ -299,7 +303,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){
m_c->object_clipper()->set_position(-1., false);
m_c->object_clipper()->set_position_by_ratio(-1., false);
});
}
}
@ -313,7 +317,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
ImGui::SameLine(drag_left_width);
ImGui::PushItemWidth(1.5 * slider_icon_width);
bool b_clp_dist_input = ImGui::BBLDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f");
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position(clp_dist, true); }
if (slider_clp_dist || b_clp_dist_input) { m_c->object_clipper()->set_position_by_ratio(clp_dist, true); }
ImGui::Separator();
m_imgui->bbl_checkbox(_L("Vertical"), m_vertical_only);

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoSeam_hpp_
#define slic3r_GLGizmoSeam_hpp_
@ -32,9 +36,9 @@ protected:
wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override;
std::string get_gizmo_entering_text() const override { return "Entering Seam painting"; }
std::string get_gizmo_leaving_text() const override { return "Leaving Seam painting"; }
std::string get_action_snapshot_name() override { return "Paint-on seam editing"; }
std::string get_gizmo_entering_text() const override { return _u8L("Entering Seam painting"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Seam painting"); }
std::string get_action_snapshot_name() const override { return _u8L("Paint-on seam editing"); }
static const constexpr float CursorRadiusMin = 0.05f; // cannot be zero
private:

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2021 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Hejl @hejllukas, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmoSimplify.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2021 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmoSimplify_hpp_
#define slic3r_GLGizmoSimplify_hpp_

View file

@ -445,8 +445,8 @@ void GLGizmoText::on_render()
m_grabbers[0].center = m_mouse_position_world;
m_grabbers[0].enabled = true;
ColorRGBA color = picking_color_component(0);
m_grabbers[0].color = color;
//ColorRGBA color = picking_color_component(0);
//m_grabbers[0].color = color;
GLShaderProgram *shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2021 Lukáš Hejl @hejllukas, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GLGizmos_hpp_
#define slic3r_GLGizmos_hpp_

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2020 - 2023 Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "GLGizmosCommon.hpp"
#include <cassert>
@ -23,10 +27,10 @@ CommonGizmosDataPool::CommonGizmosDataPool(GLCanvas3D* canvas)
using c = CommonGizmosDataID;
m_data[c::SelectionInfo].reset( new SelectionInfo(this));
m_data[c::InstancesHider].reset( new InstancesHider(this));
m_data[c::HollowedMesh].reset( new HollowedMesh(this));
// m_data[c::HollowedMesh].reset( new HollowedMesh(this));
m_data[c::Raycaster].reset( new Raycaster(this));
m_data[c::ObjectClipper].reset( new ObjectClipper(this));
m_data[c::SupportsClipper].reset( new SupportsClipper(this));
// m_data[c::SupportsClipper].reset( new SupportsClipper(this));
}
@ -59,13 +63,6 @@ InstancesHider* CommonGizmosDataPool::instances_hider() const
return inst_hider->is_valid() ? inst_hider : nullptr;
}
HollowedMesh* CommonGizmosDataPool::hollowed_mesh() const
{
HollowedMesh* hol_mesh = dynamic_cast<HollowedMesh*>(m_data.at(CommonGizmosDataID::HollowedMesh).get());
assert(hol_mesh);
return hol_mesh->is_valid() ? hol_mesh : nullptr;
}
Raycaster* CommonGizmosDataPool::raycaster() const
{
Raycaster* rc = dynamic_cast<Raycaster*>(m_data.at(CommonGizmosDataID::Raycaster).get());
@ -81,13 +78,6 @@ ObjectClipper* CommonGizmosDataPool::object_clipper() const
return (oc && oc->is_valid()) ? oc : nullptr;
}
SupportsClipper* CommonGizmosDataPool::supports_clipper() const
{
SupportsClipper* sc = dynamic_cast<SupportsClipper*>(m_data.at(CommonGizmosDataID::SupportsClipper).get());
assert(sc);
return sc->is_valid() ? sc : nullptr;
}
#ifndef NDEBUG
// Check the required resources one by one and return true if all
// dependencies are met.
@ -117,12 +107,13 @@ bool CommonGizmosDataPool::check_dependencies(CommonGizmosDataID required) const
void SelectionInfo::on_update()
{
const Selection& selection = get_pool()->get_canvas()->get_selection();
m_model_object = nullptr;
if (selection.is_single_full_instance()) {
m_model_object = selection.get_model()->objects[selection.get_object_idx()];
m_z_shift = selection.get_first_volume()->get_sla_shift_z();
}
else
m_model_object = nullptr;
}
void SelectionInfo::on_release()
@ -132,8 +123,7 @@ void SelectionInfo::on_release()
int SelectionInfo::get_active_instance() const
{
const Selection& selection = get_pool()->get_canvas()->get_selection();
return selection.get_instance_idx();
return get_pool()->get_canvas()->get_selection().get_instance_idx();
}
@ -154,7 +144,7 @@ void InstancesHider::on_update()
if (mo && active_inst != -1) {
canvas->toggle_model_objects_visibility(false);
canvas->toggle_model_objects_visibility(true, mo, active_inst);
canvas->toggle_sla_auxiliaries_visibility(m_show_supports, mo, active_inst);
canvas->toggle_sla_auxiliaries_visibility(false, mo, active_inst);
canvas->set_use_clipping_planes(true);
// Some objects may be sinking, do not show whatever is below the bed.
canvas->set_clipping_plane(0, ClippingPlane(Vec3d::UnitZ(), z_min));
@ -170,7 +160,7 @@ void InstancesHider::on_update()
for (const TriangleMesh* mesh : meshes) {
m_clippers.emplace_back(new MeshClipper);
m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), z_min));
m_clippers.back()->set_mesh(*mesh);
m_clippers.back()->set_mesh(mesh->its);
}
m_old_meshes = meshes;
}
@ -187,13 +177,6 @@ void InstancesHider::on_release()
m_clippers.clear();
}
void InstancesHider::show_supports(bool show) {
if (m_show_supports != show) {
m_show_supports = show;
on_update();
}
}
void InstancesHider::render_cut() const
{
const SelectionInfo* sel_info = get_pool()->selection_info();
@ -228,105 +211,20 @@ void InstancesHider::render_cut() const
}
void HollowedMesh::on_update()
{
const ModelObject* mo = get_pool()->selection_info()->model_object();
bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA;
if (! mo || ! is_sla)
return;
const GLCanvas3D* canvas = get_pool()->get_canvas();
const PrintObjects& print_objects = canvas->sla_print()->objects();
const SLAPrintObject* print_object = m_print_object_idx != -1
? print_objects[m_print_object_idx]
: nullptr;
// Find the respective SLAPrintObject.
if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) {
m_print_objects_count = print_objects.size();
m_print_object_idx = -1;
for (const SLAPrintObject* po : print_objects) {
++m_print_object_idx;
if (po->model_object()->id() == mo->id()) {
print_object = po;
break;
}
}
}
// If there is a valid SLAPrintObject, check state of Hollowing step.
if (print_object) {
if (print_object->is_step_done(slaposDrillHoles) && print_object->has_mesh(slaposDrillHoles)) {
size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp;
if (timestamp > m_old_hollowing_timestamp) {
const TriangleMesh& backend_mesh = print_object->get_mesh_to_slice();
if (! backend_mesh.empty()) {
m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh));
Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse();
m_hollowed_mesh_transformed->transform(trafo_inv);
m_drainholes = print_object->model_object()->sla_drain_holes;
m_old_hollowing_timestamp = timestamp;
indexed_triangle_set interior = print_object->hollowed_interior_mesh();
its_flip_triangles(interior);
m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(std::move(interior));
m_hollowed_interior_transformed->transform(trafo_inv);
}
else {
m_hollowed_mesh_transformed.reset(nullptr);
}
}
}
else
m_hollowed_mesh_transformed.reset(nullptr);
}
}
void HollowedMesh::on_release()
{
m_hollowed_mesh_transformed.reset();
m_old_hollowing_timestamp = 0;
m_print_object_idx = -1;
}
const TriangleMesh* HollowedMesh::get_hollowed_mesh() const
{
return m_hollowed_mesh_transformed.get();
}
const TriangleMesh* HollowedMesh::get_hollowed_interior() const
{
return m_hollowed_interior_transformed.get();
}
void Raycaster::on_update()
{
wxBusyCursor wait;
const ModelObject* mo = get_pool()->selection_info()->model_object();
if (! mo)
if (mo == nullptr)
return;
std::vector<const TriangleMesh*> meshes;
const std::vector<ModelVolume*>& mvs = mo->volumes;
if (mvs.size() == 1) {
assert(mvs.front()->is_model_part());
const HollowedMesh* hollowed_mesh_tracker = get_pool()->hollowed_mesh();
if (hollowed_mesh_tracker && hollowed_mesh_tracker->get_hollowed_mesh())
meshes.push_back(hollowed_mesh_tracker->get_hollowed_mesh());
}
if (meshes.empty()) {
for (const ModelVolume* mv : mvs) {
if (mv->is_model_part())
meshes.push_back(&mv->mesh());
}
}
if (meshes != m_old_meshes) {
m_raycasters.clear();
@ -351,9 +249,6 @@ std::vector<const MeshRaycaster*> Raycaster::raycasters() const
}
void ObjectClipper::on_update()
{
const ModelObject* mo = get_pool()->selection_info()->model_object();
@ -362,24 +257,19 @@ void ObjectClipper::on_update()
// which mesh should be cut?
std::vector<const TriangleMesh*> meshes;
bool has_hollowed = get_pool()->hollowed_mesh() && get_pool()->hollowed_mesh()->get_hollowed_mesh();
if (has_hollowed)
meshes.push_back(get_pool()->hollowed_mesh()->get_hollowed_mesh());
if (meshes.empty())
for (const ModelVolume* mv : mo->volumes)
meshes.push_back(&mv->mesh());
std::vector<Geometry::Transformation> trafos;
for (const ModelVolume* mv : mo->volumes) {
meshes.emplace_back(&mv->mesh());
trafos.emplace_back(mv->get_transformation());
}
if (meshes != m_old_meshes) {
m_clippers.clear();
for (const TriangleMesh* mesh : meshes) {
m_clippers.emplace_back(new MeshClipper);
m_clippers.back()->set_mesh(*mesh);
for (size_t i = 0; i < meshes.size(); ++i) {
m_clippers.emplace_back(new MeshClipper, trafos[i]);
m_clippers.back().first->set_mesh(meshes[i]->its);
}
m_old_meshes = meshes;
if (has_hollowed)
m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior());
m_old_meshes = std::move(meshes);
m_active_inst_bb_radius =
mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius();
@ -396,45 +286,74 @@ void ObjectClipper::on_release()
}
void ObjectClipper::render_cut() const
void ObjectClipper::render_cut(const std::vector<size_t>* ignore_idxs) const
{
if (m_clp_ratio == 0.)
return;
const SelectionInfo* sel_info = get_pool()->selection_info();
const ModelObject* mo = sel_info->model_object();
Geometry::Transformation inst_trafo;
bool is_assem_cnv = get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView;
inst_trafo = is_assem_cnv ?
mo->instances[sel_info->get_active_instance()]->get_assemble_transformation() :
mo->instances[sel_info->get_active_instance()]->get_transformation();
auto offset_to_assembly = mo->instances[0]->get_offset_to_assembly();
const Geometry::Transformation inst_trafo = sel_info->model_object()->instances[sel_info->get_active_instance()]->get_transformation();
size_t clipper_id = 0;
for (const ModelVolume* mv : mo->volumes) {
const Geometry::Transformation vol_trafo = mv->get_transformation();
Geometry::Transformation trafo = inst_trafo * vol_trafo;
if (is_assem_cnv) {
trafo.set_offset(trafo.get_offset() + offset_to_assembly * (GLVolume::explosion_ratio - 1.0) + vol_trafo.get_offset() * (GLVolume::explosion_ratio - 1.0));
}
else {
std::vector<size_t> ignore_idxs_local = ignore_idxs ? *ignore_idxs : std::vector<size_t>();
for (auto& clipper : m_clippers) {
Geometry::Transformation trafo = inst_trafo * clipper.second;
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
}
auto& clipper = m_clippers[clipper_id];
clipper->set_plane(*m_clp);
clipper->set_transformation(trafo);
if (is_assem_cnv)
clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), std::numeric_limits<double>::max()));
else
clipper->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
clipper.first->set_plane(*m_clp);
clipper.first->set_transformation(trafo);
clipper.first->set_limiting_plane(ClippingPlane(Vec3d::UnitZ(), -SINKING_Z_THRESHOLD));
// BBS
clipper->render_cut({ 0.25f, 0.25f, 0.25f, 1.0f });
clipper.first->render_cut({ 0.25f, 0.25f, 0.25f, 1.0f }, &ignore_idxs_local);
clipper.first->render_contour({ 1.f, 1.f, 1.f, 1.f }, &ignore_idxs_local);
++clipper_id;
// Now update the ignore idxs. Find the first element belonging to the next clipper,
// and remove everything before it and decrement everything by current number of contours.
const int num_of_contours = clipper.first->get_number_of_contours();
ignore_idxs_local.erase(ignore_idxs_local.begin(), std::find_if(ignore_idxs_local.begin(), ignore_idxs_local.end(), [num_of_contours](size_t idx) { return idx >= size_t(num_of_contours); } ));
for (size_t& idx : ignore_idxs_local)
idx -= num_of_contours;
}
}
void ObjectClipper::set_position(double pos, bool keep_normal)
int ObjectClipper::get_number_of_contours() const
{
int sum = 0;
for (const auto& [clipper, trafo] : m_clippers)
sum += clipper->get_number_of_contours();
return sum;
}
int ObjectClipper::is_projection_inside_cut(const Vec3d& point) const
{
if (m_clp_ratio == 0.)
return -1;
int idx_offset = 0;
for (const auto& [clipper, trafo] : m_clippers) {
if (int idx = clipper->is_projection_inside_cut(point); idx != -1)
return idx_offset + idx;
idx_offset += clipper->get_number_of_contours();
}
return -1;
}
bool ObjectClipper::has_valid_contour() const
{
return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto& cl) { return cl.first->has_valid_contour(); });
}
std::vector<Vec3d> ObjectClipper::point_per_contour() const
{
std::vector<Vec3d> pts;
for (const auto& clipper : m_clippers) {
const std::vector<Vec3d> pts_clipper = clipper.first->point_per_contour();
pts.insert(pts.end(), pts_clipper.begin(), pts_clipper.end());;
}
return pts;
}
void ObjectClipper::set_position_by_ratio(double pos, bool keep_normal)
{
const ModelObject* mo = get_pool()->selection_info()->model_object();
int active_inst = get_pool()->selection_info()->get_active_instance();
@ -476,107 +395,19 @@ void ObjectClipper::set_range_and_pos(const Vec3d &cpl_normal, double cpl_offset
get_pool()->get_canvas()->set_as_dirty();
}
bool ObjectClipper::is_projection_inside_cut(const Vec3d &point) const
const ClippingPlane* ObjectClipper::get_clipping_plane(bool ignore_hide_clipped) const
{
return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [point](const auto &cl) {
return cl->is_projection_inside_cut(point);
});
static const ClippingPlane no_clip = ClippingPlane::ClipsNothing();
return (ignore_hide_clipped || m_hide_clipped) ? m_clp.get() : &no_clip;
}
bool ObjectClipper::has_valid_contour() const
void ObjectClipper::set_behavior(bool hide_clipped, bool fill_cut, double contour_width)
{
return m_clp_ratio != 0. && std::any_of(m_clippers.begin(), m_clippers.end(), [](const auto &cl) {
return cl->has_valid_contour();
});
m_hide_clipped = hide_clipped;
for (auto& clipper : m_clippers)
clipper.first->set_behaviour(fill_cut, contour_width);
}
void SupportsClipper::on_update()
{
const ModelObject* mo = get_pool()->selection_info()->model_object();
bool is_sla = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA;
if (! mo || ! is_sla)
return;
const GLCanvas3D* canvas = get_pool()->get_canvas();
const PrintObjects& print_objects = canvas->sla_print()->objects();
const SLAPrintObject* print_object = m_print_object_idx != -1
? print_objects[m_print_object_idx]
: nullptr;
// Find the respective SLAPrintObject.
if (m_print_object_idx < 0 || m_print_objects_count != int(print_objects.size())) {
m_print_objects_count = print_objects.size();
m_print_object_idx = -1;
for (const SLAPrintObject* po : print_objects) {
++m_print_object_idx;
if (po->model_object()->id() == mo->id()) {
print_object = po;
break;
}
}
}
if (print_object
&& print_object->is_step_done(slaposSupportTree)
&& ! print_object->support_mesh().empty())
{
// If the supports are already calculated, save the timestamp of the respective step
// so we can later tell they were recalculated.
size_t timestamp = print_object->step_state_with_timestamp(slaposSupportTree).timestamp;
if (! m_clipper || timestamp != m_old_timestamp) {
// The timestamp has changed.
m_clipper.reset(new MeshClipper);
// The mesh should already have the shared vertices calculated.
m_clipper->set_mesh(print_object->support_mesh());
m_old_timestamp = timestamp;
}
}
else
// The supports are not valid. We better dump the cached data.
m_clipper.reset();
}
void SupportsClipper::on_release()
{
m_clipper.reset();
m_old_timestamp = 0;
m_print_object_idx = -1;
}
void SupportsClipper::render_cut() const
{
const CommonGizmosDataObjects::ObjectClipper* ocl = get_pool()->object_clipper();
if (ocl->get_position() == 0.
|| ! get_pool()->instances_hider()->are_supports_shown()
|| ! m_clipper)
return;
const SelectionInfo* sel_info = get_pool()->selection_info();
const ModelObject* mo = sel_info->model_object();
const Geometry::Transformation inst_trafo = mo->instances[sel_info->get_active_instance()]->get_transformation();
//Geometry::Transformation vol_trafo = mo->volumes.front()->get_transformation();
Geometry::Transformation trafo = inst_trafo;// * vol_trafo;
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., sel_info->get_sla_shift()));
// Get transformation of supports
Geometry::Transformation supports_trafo = trafo;
supports_trafo.set_scaling_factor(Vec3d::Ones());
supports_trafo.set_offset(Vec3d(trafo.get_offset()(0), trafo.get_offset()(1), sel_info->get_sla_shift()));
supports_trafo.set_rotation(Vec3d(0., 0., trafo.get_rotation()(2)));
// I don't know why, but following seems to be correct.
supports_trafo.set_mirror(Vec3d(trafo.get_mirror()(0) * trafo.get_mirror()(1) * trafo.get_mirror()(2),
1,
1.));
m_clipper->set_plane(*ocl->get_clipping_plane());
m_clipper->set_transformation(supports_trafo);
m_clipper->render_cut({ 1.0f, 0.f, 0.37f, 1.0f });
}
using namespace AssembleViewDataObjects;
AssembleViewDataPool::AssembleViewDataPool(GLCanvas3D* canvas)
@ -683,7 +514,7 @@ void ModelObjectsClipper::on_update()
m_clippers.clear();
for (const TriangleMesh* mesh : meshes) {
m_clippers.emplace_back(new MeshClipper);
m_clippers.back()->set_mesh(*mesh);
m_clippers.back()->set_mesh(mesh->its);
}
m_old_meshes = meshes;

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2020 - 2023 Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Hejl @hejllukas
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_GLGizmosCommon_hpp_
#define slic3r_GUI_GLGizmosCommon_hpp_
@ -6,12 +10,13 @@
#include "slic3r/GUI/3DScene.hpp"
#include "slic3r/GUI/MeshUtils.hpp"
#include "libslic3r/SLA/Hollowing.hpp"
namespace Slic3r {
class ModelObject;
class ModelInstance;
class SLAPrintObject;
class ModelVolume;
namespace GUI {
@ -67,10 +72,8 @@ enum class CommonGizmosDataID {
None = 0,
SelectionInfo = 1 << 0,
InstancesHider = 1 << 1,
HollowedMesh = 1 << 2,
Raycaster = 1 << 3,
ObjectClipper = 1 << 4,
SupportsClipper = 1 << 5,
};
@ -89,10 +92,10 @@ public:
// Getters for the data that need to be accessed from the gizmos directly.
CommonGizmosDataObjects::SelectionInfo* selection_info() const;
CommonGizmosDataObjects::InstancesHider* instances_hider() const;
CommonGizmosDataObjects::HollowedMesh* hollowed_mesh() const;
// CommonGizmosDataObjects::HollowedMesh* hollowed_mesh() const;
CommonGizmosDataObjects::Raycaster* raycaster() const;
CommonGizmosDataObjects::ObjectClipper* object_clipper() const;
CommonGizmosDataObjects::SupportsClipper* supports_clipper() const;
// CommonGizmosDataObjects::SupportsClipper* supports_clipper() const;
GLCanvas3D* get_canvas() const { return m_canvas; }
@ -141,7 +144,6 @@ protected:
virtual void on_update() = 0;
CommonGizmosDataPool* get_pool() const { return m_common; }
private:
bool m_is_valid = false;
CommonGizmosDataPool* m_common = nullptr;
@ -185,8 +187,6 @@ public:
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
#endif // NDEBUG
void show_supports(bool show);
bool are_supports_shown() const { return m_show_supports; }
void render_cut() const;
protected:
@ -194,42 +194,12 @@ protected:
void on_release() override;
private:
bool m_show_supports = false;
std::vector<const TriangleMesh*> m_old_meshes;
std::vector<std::unique_ptr<MeshClipper>> m_clippers;
};
class HollowedMesh : public CommonGizmosDataBase
{
public:
explicit HollowedMesh(CommonGizmosDataPool* cgdp)
: CommonGizmosDataBase(cgdp) {}
#ifndef NDEBUG
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
#endif // NDEBUG
const sla::DrainHoles &get_drainholes() const { return m_drainholes; }
const TriangleMesh* get_hollowed_mesh() const;
const TriangleMesh* get_hollowed_interior() const;
protected:
void on_update() override;
void on_release() override;
private:
std::unique_ptr<TriangleMesh> m_hollowed_mesh_transformed;
std::unique_ptr<TriangleMesh> m_hollowed_interior_transformed;
size_t m_old_hollowing_timestamp = 0;
int m_print_object_idx = -1;
int m_print_objects_count = 0;
sla::DrainHoles m_drainholes;
};
class Raycaster : public CommonGizmosDataBase
{
public:
@ -261,57 +231,31 @@ public:
#ifndef NDEBUG
CommonGizmosDataID get_dependencies() const override { return CommonGizmosDataID::SelectionInfo; }
#endif // NDEBUG
void set_position(double pos, bool keep_normal);
double get_position() const { return m_clp_ratio; }
ClippingPlane* get_clipping_plane() const { return m_clp.get(); }
void render_cut() const;
const ClippingPlane* get_clipping_plane(bool ignore_hide_clipped = false) const;
void render_cut(const std::vector<size_t>* ignore_idxs = nullptr) const;
void set_position_by_ratio(double pos, bool keep_normal);
void set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos);
void set_behavior(bool hide_clipped, bool fill_cut, double contour_width);
bool is_projection_inside_cut(const Vec3d &point_in) const;
int get_number_of_contours() const;
std::vector<Vec3d> point_per_contour() const;
int is_projection_inside_cut(const Vec3d& point_in) const;
bool has_valid_contour() const;
protected:
void on_update() override;
void on_release() override;
private:
std::vector<const TriangleMesh*> m_old_meshes;
std::vector<std::unique_ptr<MeshClipper>> m_clippers;
std::vector<std::pair<std::unique_ptr<MeshClipper>, Geometry::Transformation>> m_clippers;
std::unique_ptr<ClippingPlane> m_clp;
double m_clp_ratio = 0.;
double m_active_inst_bb_radius = 0.;
};
class SupportsClipper : public CommonGizmosDataBase
{
public:
explicit SupportsClipper(CommonGizmosDataPool* cgdp)
: CommonGizmosDataBase(cgdp) {}
#ifndef NDEBUG
CommonGizmosDataID get_dependencies() const override {
return CommonGizmosDataID(
int(CommonGizmosDataID::SelectionInfo)
| int(CommonGizmosDataID::ObjectClipper)
);
}
#endif // NDEBUG
void render_cut() const;
protected:
void on_update() override;
void on_release() override;
private:
size_t m_old_timestamp = 0;
int m_print_object_idx = -1;
int m_print_objects_count = 0;
std::unique_ptr<MeshClipper> m_clipper;
bool m_hide_clipped = true;
};
} // namespace CommonGizmosDataObjects

View file

@ -1,3 +1,8 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, David Kocík @kocikdav, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas
///|/ Copyright (c) 2019 John Drake @foxox
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "libslic3r/libslic3r.h"
#include "GLGizmosManager.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
@ -269,20 +274,13 @@ float GLGizmosManager::get_layout_scale()
return m_layout.scale;
}
bool GLGizmosManager::init_arrow(const BackgroundTexture::Metadata& arrow_texture)
bool GLGizmosManager::init_arrow(const std::string& filename)
{
if (m_arrow_texture.texture.get_id() != 0)
if (m_arrow_texture.get_id() != 0)
return true;
std::string path = resources_dir() + "/images/";
bool res = false;
if (!arrow_texture.filename.empty())
res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000);
if (res)
m_arrow_texture.metadata = arrow_texture;
return res;
const std::string path = resources_dir() + "/images/";
return (!filename.empty()) ? m_arrow_texture.load_from_svg_file(path + filename, false, false, false, 1000) : false;
}
void GLGizmosManager::set_overlay_icon_size(float size)
@ -378,7 +376,7 @@ void GLGizmosManager::update_data()
m_common_gizmos_data->update(get_current()
? get_current()->get_requirements()
: CommonGizmosDataID(0));
if (m_current != Undefined) m_gizmos[m_current]->data_changed();
if (m_current != Undefined) m_gizmos[m_current]->data_changed(m_serializing);
//BBS: GUI refactor: add object manipulation in gizmo
m_object_manipulation.update_ui_from_settings();
@ -600,10 +598,12 @@ bool GLGizmosManager::gizmos_toolbar_on_mouse(const wxMouseEvent &mouse_event) {
mc.left = true;
open_gizmo(gizmo);
return true;
} else if (mouse_event.RightDown()) {
}
else if (mouse_event.RightDown()) {
mc.right = true;
return true;
} else if (mouse_event.MiddleDown()) {
}
else if (mouse_event.MiddleDown()) {
mc.middle = true;
return true;
}
@ -619,13 +619,16 @@ bool GLGizmosManager::gizmos_toolbar_on_mouse(const wxMouseEvent &mouse_event) {
}
// draging start on toolbar so no propagation into scene
return true;
} else if (mc.left && mouse_event.LeftUp()) {
}
else if (mc.left && mouse_event.LeftUp()) {
mc.left = false;
return true;
} else if (mc.right && mouse_event.RightUp()) {
}
else if (mc.right && mouse_event.RightUp()) {
mc.right = false;
return true;
} else if (mc.middle && mouse_event.MiddleUp()) {
}
else if (mc.middle && mouse_event.MiddleUp()) {
mc.middle = false;
return true;
}
@ -662,8 +665,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
bool processed = false;
if ((evt.GetModifiers() & ctrlMask) != 0)
{
if ((evt.GetModifiers() & ctrlMask) != 0) {
switch (keyCode)
{
#ifdef __APPLE__
@ -681,8 +683,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
}
}
}
else if (!evt.HasModifiers())
{
else if (!evt.HasModifiers()) {
switch (keyCode)
{
// key ESC
@ -775,8 +776,7 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
}
}
if (!processed && !evt.HasModifiers())
{
if (!processed && !evt.HasModifiers()) {
if (handle_shortcut(keyCode))
processed = true;
}
@ -792,15 +792,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
const int keyCode = evt.GetKeyCode();
bool processed = false;
// todo: zhimin Each gizmo should handle key event in it own on_key() function
if (m_current == Cut) {
if (GLGizmoAdvancedCut *gizmo_cut = dynamic_cast<GLGizmoAdvancedCut *>(get_current())) {
return gizmo_cut->on_key(evt);
}
}
if (evt.GetEventType() == wxEVT_KEY_UP)
{
if (evt.GetEventType() == wxEVT_KEY_UP) {
/*if (m_current == SlaSupports || m_current == Hollow)
{
bool is_editing = true;
@ -987,7 +979,7 @@ void GLGizmosManager::render_background(float left, float top, float right, floa
void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_type) const
{
std::vector<size_t> selectable_idxs = get_selectable_idxs();
const std::vector<size_t> selectable_idxs = get_selectable_idxs();
if (selectable_idxs.empty())
return;
float cnv_w = (float)m_parent.get_canvas_size().get_width();
@ -1006,18 +998,18 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t
if (idx == highlighted_type) {
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
unsigned int tex_id = m_arrow_texture.texture.get_id();
unsigned int tex_id = m_arrow_texture.get_id();
float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f;
float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f;
float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width;
float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width;
float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height;
float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height;
const float left_uv = 0.0f;
const float right_uv = 1.0f;
const float top_uv = 1.0f;
const float bottom_uv = 0.0f;
float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
float arrow_sides_ratio = (float)m_arrow_texture.get_height() / (float)m_arrow_texture.get_width();
GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * 2.2f * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size * 1.6f , zoomed_top_y + zoomed_icons_size * 0.6f, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } });
GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * 2.2f * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size * 1.6f , zoomed_top_y + zoomed_icons_size * 0.6f, { { left_uv, bottom_uv }, { left_uv, top_uv }, { right_uv, top_uv }, { right_uv, bottom_uv } });
break;
}
zoomed_top_y -= zoomed_stride_y;
@ -1170,9 +1162,12 @@ bool GLGizmosManager::generate_icons_texture()
{
std::string path = resources_dir() + "/images/";
std::vector<std::string> filenames;
for (size_t idx = 0; idx<m_gizmos.size(); ++idx) {
if (m_gizmos[idx] != nullptr) {
const std::string& icon_filename = m_gizmos[idx]->get_icon_filename();
for (size_t idx=0; idx<m_gizmos.size(); ++idx)
{
auto &gizmo = m_gizmos[idx];
if (gizmo != nullptr)
{
const std::string& icon_filename = gizmo->get_icon_filename();
if (!icon_filename.empty())
filenames.push_back(path + icon_filename);
}

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2022 Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, David Kocík @kocikdav, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_GUI_GLGizmosManager_hpp_
#define slic3r_GUI_GLGizmosManager_hpp_
@ -116,10 +120,10 @@ private:
GLCanvas3D& m_parent;
bool m_enabled;
std::vector<std::unique_ptr<GLGizmoBase>> m_gizmos;
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
GLTexture m_icons_texture;
bool m_icons_texture_dirty;
BackgroundTexture m_background_texture;
BackgroundTexture m_arrow_texture;
GLTexture m_arrow_texture;
Layout m_layout;
EType m_current;
EType m_hover;
@ -175,7 +179,7 @@ public:
float get_layout_scale();
bool init_arrow(const BackgroundTexture::Metadata& arrow_texture);
bool init_arrow(const std::string& filename);
template<class Archive>
void load(Archive& ar)

View file

@ -1,3 +1,7 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include "MeshUtils.hpp"
#include "libslic3r/Tesselate.hpp"
@ -5,25 +9,39 @@
#include "libslic3r/TriangleMeshSlicer.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/CSGMesh/SliceCSGMesh.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/Camera.hpp"
#include "slic3r/GUI/CameraUtils.hpp"
#include <GL/glew.h>
#include <igl/unproject.h>
#include "CameraUtils.hpp"
#include <cstdint>
namespace Slic3r {
namespace GUI {
void MeshClipper::set_behaviour(bool fill_cut, double contour_width)
{
if (fill_cut != m_fill_cut || ! is_approx(contour_width, m_contour_width))
m_result.reset();
m_fill_cut = fill_cut;
m_contour_width = contour_width;
}
void MeshClipper::set_plane(const ClippingPlane& plane)
{
if (m_plane != plane) {
m_plane = plane;
m_triangles_valid = false;
m_result.reset();
}
}
@ -32,27 +50,41 @@ void MeshClipper::set_limiting_plane(const ClippingPlane& plane)
{
if (m_limiting_plane != plane) {
m_limiting_plane = plane;
m_triangles_valid = false;
m_result.reset();
}
}
void MeshClipper::set_mesh(const TriangleMesh& mesh)
void MeshClipper::set_mesh(const indexed_triangle_set& mesh)
{
if (m_mesh != &mesh) {
if (m_mesh.get() != &mesh) {
m_mesh = &mesh;
m_triangles_valid = false;
m_triangles2d.resize(0);
m_result.reset();
}
}
void MeshClipper::set_negative_mesh(const TriangleMesh& mesh)
void MeshClipper::set_mesh(AnyPtr<const indexed_triangle_set> &&ptr)
{
if (m_negative_mesh != &mesh) {
if (m_mesh.get() != ptr.get()) {
m_mesh = std::move(ptr);
m_result.reset();
}
}
void MeshClipper::set_negative_mesh(const indexed_triangle_set& mesh)
{
if (m_negative_mesh.get() != &mesh) {
m_negative_mesh = &mesh;
m_triangles_valid = false;
m_triangles2d.resize(0);
m_result.reset();
}
}
void MeshClipper::set_negative_mesh(AnyPtr<const indexed_triangle_set> &&ptr)
{
if (m_negative_mesh.get() != ptr.get()) {
m_negative_mesh = std::move(ptr);
m_result.reset();
}
}
@ -62,20 +94,43 @@ void MeshClipper::set_transformation(const Geometry::Transformation& trafo)
{
if (! m_trafo.get_matrix().isApprox(trafo.get_matrix())) {
m_trafo = trafo;
m_triangles_valid = false;
m_triangles2d.resize(0);
m_result.reset();
}
}
void MeshClipper::render_cut(const ColorRGBA& color)
void MeshClipper::render_cut(const ColorRGBA& color, const std::vector<size_t>* ignore_idxs)
{
if (! m_triangles_valid)
if (! m_result)
recalculate_triangles();
GLShaderProgram* curr_shader = wxGetApp().get_current_shader();
if (curr_shader != nullptr)
curr_shader->stop_using();
if (m_model.vertices_count() == 0 || m_model.indices_count() == 0)
return;
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader != nullptr) {
shader->start_using();
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
for (size_t i=0; i<m_result->cut_islands.size(); ++i) {
if (ignore_idxs && std::binary_search(ignore_idxs->begin(), ignore_idxs->end(), i))
continue;
CutIsland& isl = m_result->cut_islands[i];
isl.model.set_color(isl.disabled ? ColorRGBA(0.5f, 0.5f, 0.5f, 1.f) : color);
isl.model.render();
}
shader->stop_using();
}
if (curr_shader != nullptr)
curr_shader->start_using();
}
void MeshClipper::render_contour(const ColorRGBA& color, const std::vector<size_t>* ignore_idxs)
{
if (! m_result)
recalculate_triangles();
GLShaderProgram* curr_shader = wxGetApp().get_current_shader();
if (curr_shader != nullptr)
@ -87,8 +142,13 @@ void MeshClipper::render_cut(const ColorRGBA& color)
const Camera& camera = wxGetApp().plater()->get_camera();
shader->set_uniform("view_model_matrix", camera.get_view_matrix());
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
m_model.set_color(color);
m_model.render();
for (size_t i=0; i<m_result->cut_islands.size(); ++i) {
if (ignore_idxs && std::binary_search(ignore_idxs->begin(), ignore_idxs->end(), i))
continue;
CutIsland& isl = m_result->cut_islands[i];
isl.model_expanded.set_color(isl.disabled ? ColorRGBA(1.f, 0.f, 0.f, 1.f) : color);
isl.model_expanded.render();
}
shader->stop_using();
}
@ -96,18 +156,19 @@ void MeshClipper::render_cut(const ColorRGBA& color)
curr_shader->start_using();
}
bool MeshClipper::is_projection_inside_cut(const Vec3d &point_in) const
int MeshClipper::is_projection_inside_cut(const Vec3d& point_in) const
{
if (!m_result || m_result->cut_islands.empty())
return false;
return -1;
Vec3d point = m_result->trafo.inverse() * point_in;
Point pt_2d = Point::new_scale(Vec2d(point.x(), point.y()));
for (const CutIsland &isl : m_result->cut_islands) {
for (int i=0; i<int(m_result->cut_islands.size()); ++i) {
const CutIsland& isl = m_result->cut_islands[i];
if (isl.expoly_bb.contains(pt_2d) && isl.expoly.contains(pt_2d))
return true;
return i; // TODO: handle intersecting contours
}
return false;
return -1;
}
bool MeshClipper::has_valid_contour() const
@ -115,25 +176,74 @@ bool MeshClipper::has_valid_contour() const
return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& isl) { return !isl.expoly.empty(); });
}
std::vector<Vec3d> MeshClipper::point_per_contour() const
{
assert(m_result);
std::vector<Vec3d> out;
for (const CutIsland& isl : m_result->cut_islands) {
assert(isl.expoly.contour.size() > 2);
// Now return a point lying inside the contour but not in a hole.
// We do this by taking a point lying close to the edge, repeating
// this several times for different edges and distances from them.
// (We prefer point not extremely close to the border.
bool done = false;
Vec2d p;
size_t i = 1;
while (i < isl.expoly.contour.size()) {
const Vec2d& a = unscale(isl.expoly.contour.points[i-1]);
const Vec2d& b = unscale(isl.expoly.contour.points[i]);
Vec2d n = (b-a).normalized();
std::swap(n.x(), n.y());
n.x() = -1 * n.x();
double f = 10.;
while (f > 0.05) {
p = (0.5*(b+a)) + f * n;
if (isl.expoly.contains(Point::new_scale(p))) {
done = true;
break;
}
f = f/10.;
}
if (done)
break;
i += std::max(size_t(2), isl.expoly.contour.size() / 5);
}
// If the above failed, just return the centroid, regardless of whether
// it is inside the contour or in a hole (we must return something).
Vec2d c = done ? p : unscale(isl.expoly.contour.centroid());
out.emplace_back(m_result->trafo * Vec3d(c.x(), c.y(), 0.));
}
return out;
}
void MeshClipper::recalculate_triangles()
{
const Transform3f& instance_matrix_no_translation_no_scaling = m_trafo.get_matrix(true,false,true).cast<float>();
// Calculate clipping plane normal in mesh coordinates.
const Vec3f up_noscale = instance_matrix_no_translation_no_scaling.inverse() * m_plane.get_normal().cast<float>();
const Vec3d up = up_noscale.cast<double>().cwiseProduct(m_trafo.get_scaling_factor());
// Calculate distance from mesh origin to the clipping plane (in mesh coordinates).
const float height_mesh = m_plane.distance(m_trafo.get_offset()) * (up_noscale.norm()/up.norm());
m_result = ClipResult();
auto plane_mesh = Eigen::Hyperplane<double, 3>(m_plane.get_normal(), -m_plane.distance(Vec3d::Zero())).transform(m_trafo.get_matrix().inverse());
const Vec3d up = plane_mesh.normal();
const float height_mesh = -plane_mesh.offset();
// Now do the cutting
MeshSlicingParams slicing_params;
slicing_params.trafo.rotate(Eigen::Quaternion<double, Eigen::DontAlign>::FromTwoVectors(up, Vec3d::UnitZ()));
ExPolygons expolys = union_ex(slice_mesh(m_mesh->its, height_mesh, slicing_params));
ExPolygons expolys;
if (m_csgmesh.empty()) {
if (m_mesh)
expolys = union_ex(slice_mesh(*m_mesh, height_mesh, slicing_params));
if (m_negative_mesh && !m_negative_mesh->empty()) {
const ExPolygons neg_expolys = union_ex(slice_mesh(m_negative_mesh->its, height_mesh, slicing_params));
const ExPolygons neg_expolys = union_ex(slice_mesh(*m_negative_mesh, height_mesh, slicing_params));
expolys = diff_ex(expolys, neg_expolys);
}
} else {
expolys = std::move(csg::slice_csgmesh_ex(range(m_csgmesh), {height_mesh}, MeshSlicingParamsEx{slicing_params}).front());
}
// Triangulate and rotate the cut into world coords:
Eigen::Quaterniond q;
@ -142,7 +252,6 @@ void MeshClipper::recalculate_triangles()
tr.rotate(q);
tr = m_trafo.get_matrix() * tr;
m_result = ClipResult();
m_result->trafo = tr;
if (m_limiting_plane != ClippingPlane::ClipsNothing())
@ -190,7 +299,7 @@ void MeshClipper::recalculate_triangles()
// it so it lies on our line. This will be the figure to subtract
// from the cut. The coordinates must not overflow after the transform,
// make the rectangle a bit smaller.
const coord_t size = (std::numeric_limits<coord_t>::max() - scale_(std::max(std::abs(e*a), std::abs(e*b)))) / 4;
const coord_t size = (std::numeric_limits<coord_t>::max()/2 - scale_(std::max(std::abs(e * a), std::abs(e * b)))) / 4;
Polygons ep {Polygon({Point(-size, 0), Point(size, 0), Point(size, 2*size), Point(-size, 2*size)})};
ep.front().rotate(angle);
ep.front().translate(scale_(-e * a), scale_(-e * b));
@ -198,37 +307,107 @@ void MeshClipper::recalculate_triangles()
}
}
tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting
Transform3d tr2 = tr;
tr2.pretranslate(0.002 * m_plane.get_normal().normalized());
std::vector<Vec2f> triangles2d;
for (const ExPolygon& exp : expolys) {
triangles2d.clear();
m_result->cut_islands.push_back(CutIsland());
CutIsland& isl = m_result->cut_islands.back();
isl.expoly = std::move(exp);
isl.expoly_bb = get_extents(exp);
}
m_triangles2d = triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.);
tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting
m_model.reset();
if (m_fill_cut) {
triangles2d = triangulate_expolygon_2f(exp, m_trafo.get_matrix().matrix().determinant() < 0.);
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
init_data.reserve_vertices(m_triangles2d.size());
init_data.reserve_indices(m_triangles2d.size());
init_data.reserve_vertices(triangles2d.size());
init_data.reserve_indices(triangles2d.size());
// vertices + indices
for (auto it = m_triangles2d.cbegin(); it != m_triangles2d.cend(); it = it + 3) {
for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) {
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
const size_t idx = it - m_triangles2d.cbegin();
const size_t idx = it - triangles2d.cbegin();
init_data.add_triangle((unsigned int)idx, (unsigned int)idx + 1, (unsigned int)idx + 2);
}
if (!init_data.is_empty())
m_model.init_from(std::move(init_data));
isl.model.init_from(std::move(init_data));
}
m_triangles_valid = true;
if (m_contour_width != 0. && ! exp.contour.empty()) {
triangles2d.clear();
// The contours must not scale with the object. Check the scale factor
// in the respective directions, create a scaled copy of the ExPolygon
// offset it and then unscale the result again.
Transform3d t = tr;
t.translation() = Vec3d::Zero();
double scale_x = (t * Vec3d::UnitX()).norm();
double scale_y = (t * Vec3d::UnitY()).norm();
// To prevent overflow after scaling, downscale the input if needed:
double extra_scale = 1.;
int32_t limit = int32_t(std::min(std::numeric_limits<coord_t>::max() / (2. * std::max(1., scale_x)), std::numeric_limits<coord_t>::max() / (2. * std::max(1., scale_y))));
int32_t max_coord = 0;
for (const Point& pt : exp.contour)
max_coord = std::max(max_coord, std::max(std::abs(pt.x()), std::abs(pt.y())));
if (max_coord + m_contour_width >= limit)
extra_scale = 0.9 * double(limit) / max_coord;
ExPolygon exp_copy = exp;
if (extra_scale != 1.)
exp_copy.scale(extra_scale);
exp_copy.scale(scale_x, scale_y);
ExPolygons expolys_exp = offset_ex(exp_copy, scale_(m_contour_width));
expolys_exp = diff_ex(expolys_exp, ExPolygons({exp_copy}));
for (ExPolygon& e : expolys_exp) {
e.scale(1./scale_x, 1./scale_y);
if (extra_scale != 1.)
e.scale(1./extra_scale);
}
triangles2d = triangulate_expolygons_2f(expolys_exp, m_trafo.get_matrix().matrix().determinant() < 0.);
GLModel::Geometry init_data = GLModel::Geometry();
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
init_data.reserve_vertices(triangles2d.size());
init_data.reserve_indices(triangles2d.size());
// vertices + indices
for (auto it = triangles2d.cbegin(); it != triangles2d.cend(); it = it + 3) {
init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 0)).x(), (*(it + 0)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 1)).x(), (*(it + 1)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
init_data.add_vertex((Vec3f)(tr2 * Vec3d((*(it + 2)).x(), (*(it + 2)).y(), height_mesh)).cast<float>(), (Vec3f)up.cast<float>());
const size_t idx = it - triangles2d.cbegin();
init_data.add_triangle((unsigned short)idx, (unsigned short)idx + 1, (unsigned short)idx + 2);
}
if (!init_data.is_empty())
isl.model_expanded.init_from(std::move(init_data));
}
isl.expoly = std::move(exp);
isl.expoly_bb = get_extents(isl.expoly);
Point centroid_scaled = isl.expoly.contour.centroid();
Vec3d centroid_world = m_result->trafo * Vec3d(unscale(centroid_scaled).x(), unscale(centroid_scaled).y(), 0.);
isl.hash = isl.expoly.contour.size() + size_t(std::abs(100.*centroid_world.x())) + size_t(std::abs(100.*centroid_world.y())) + size_t(std::abs(100.*centroid_world.z()));
}
// Now sort the islands so they are in defined order. This is a hack needed by cut gizmo, which sometimes
// flips the normal of the cut, in which case the contours stay the same but their order may change.
std::sort(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland& a, const CutIsland& b) {
return a.hash < b.hash;
});
}
@ -237,7 +416,7 @@ Vec3f MeshRaycaster::get_triangle_normal(size_t facet_idx) const
return m_normals[facet_idx];
}
void MeshRaycaster::line_from_mouse_pos_static(const Vec2d &mouse_pos, const Transform3d &trafo, const Camera &camera, Vec3d &point, Vec3d &direction)
void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera, Vec3d& point, Vec3d& direction)
{
CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction);
Transform3d inv = trafo.inverse();
@ -245,38 +424,18 @@ void MeshRaycaster::line_from_mouse_pos_static(const Vec2d &mouse_pos, const Tra
direction = inv.linear()*direction;
}
void MeshRaycaster::line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3d& point, Vec3d& direction) const
{
Matrix4d modelview = camera.get_view_matrix().matrix();
Matrix4d projection= camera.get_projection_matrix().matrix();
Vec4i viewport(camera.get_viewport().data());
Vec3d pt1;
Vec3d pt2;
igl::unproject(Vec3d(mouse_pos(0), viewport[3] - mouse_pos(1), 0.),
modelview, projection, viewport, pt1);
igl::unproject(Vec3d(mouse_pos(0), viewport[3] - mouse_pos(1), 1.),
modelview, projection, viewport, pt2);
Transform3d inv = trafo.inverse();
pt1 = inv * pt1;
pt2 = inv * pt2;
point = pt1;
direction = pt2-pt1;
}
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane,
size_t* facet_idx, bool sinking_limit) const
{
Vec3d point;
Vec3d direction;
line_from_mouse_pos(mouse_pos, trafo, camera, point, direction);
CameraUtils::ray_from_screen_pos(camera, mouse_pos, point, direction);
Transform3d inv = trafo.inverse();
point = inv*point;
direction = inv.linear()*direction;
std::vector<sla::IndexedMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction);
std::vector<AABBMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction);
if (hits.empty())
return false; // no intersection found
@ -309,6 +468,21 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
}
bool MeshRaycaster::intersects_line(Vec3d point, Vec3d direction, const Transform3d& trafo) const
{
Transform3d trafo_inv = trafo.inverse();
Vec3d to = trafo_inv * (point + direction);
point = trafo_inv * point;
direction = (to-point).normalized();
std::vector<AABBMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction);
std::vector<AABBMesh::hit_result> neg_hits = m_emesh.query_ray_hits(point, -direction);
return !hits.empty() || !neg_hits.empty();
}
std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector<Vec3f>& points,
const ClippingPlane* clipping_plane) const
{
@ -327,7 +501,7 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
bool is_obscured = false;
// Cast a ray in the direction of the camera and look for intersection with the mesh:
std::vector<sla::IndexedMesh::hit_result> hits;
std::vector<AABBMesh::hit_result> hits;
// Offset the start of the ray by EPSILON to account for numerical inaccuracies.
hits = m_emesh.query_ray_hits((inverse_trafo * pt.cast<double>() + direction_to_camera_mesh * EPSILON),
direction_to_camera_mesh);
@ -364,7 +538,7 @@ bool MeshRaycaster::closest_hit(const Vec2d& mouse_pos, const Transform3d& trafo
Vec3d direction;
line_from_mouse_pos(mouse_pos, trafo, camera, point, direction);
const std::vector<sla::IndexedMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction.normalized());
const std::vector<AABBMesh::hit_result> hits = m_emesh.query_ray_hits(point, direction.normalized());
if (hits.empty())
return false; // no intersection found
@ -379,7 +553,7 @@ bool MeshRaycaster::closest_hit(const Vec2d& mouse_pos, const Transform3d& trafo
if (hit_id == hits.size())
return false; // all points are obscured or cut by the clipping plane.
const sla::IndexedMesh::hit_result& hit = hits[hit_id];
const AABBMesh::hit_result& hit = hits[hit_id];
position = hit.position().cast<float>();
normal = hit.normal().cast<float>();
@ -394,8 +568,10 @@ Vec3f MeshRaycaster::get_closest_point(const Vec3f& point, Vec3f* normal) const
{
int idx = 0;
Vec3d closest_point;
m_emesh.squared_distance(point.cast<double>(), idx, closest_point);
Vec3d pointd = point.cast<double>();
m_emesh.squared_distance(pointd, idx, closest_point);
if (normal)
// TODO: consider: get_normal(m_emesh, pointd).cast<float>();
*normal = m_normals[idx];
return closest_point.cast<float>();

View file

@ -1,18 +1,25 @@
///|/ Copyright (c) Prusa Research 2019 - 2023 Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Enrico Turri @enricoturri1966, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_MeshUtils_hpp_
#define slic3r_MeshUtils_hpp_
#include "libslic3r/Point.hpp"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/SLA/IndexedMesh.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/AABBMesh.hpp"
#include "libslic3r/CSGMesh/TriangleMeshAdapter.hpp"
#include "libslic3r/CSGMesh/CSGMeshCopy.hpp"
#include "admesh/stl.h"
#include "slic3r/GUI/GLModel.hpp"
#include <cfloat>
#include <optional>
#include <memory>
namespace Slic3r {
namespace GUI {
struct Camera;
@ -71,6 +78,10 @@ public:
class MeshClipper
{
public:
// Set whether the cut should be triangulated and whether a cut
// contour should be calculated and shown.
void set_behaviour(bool fill_cut, double contour_width);
// Inform MeshClipper about which plane we want to use to cut the mesh
// This is supposed to be in world coordinates.
void set_plane(const ClippingPlane& plane);
@ -82,9 +93,25 @@ public:
// Which mesh to cut. MeshClipper remembers const * to it, caller
// must make sure that it stays valid.
void set_mesh(const TriangleMesh& mesh);
void set_mesh(const indexed_triangle_set& mesh);
void set_mesh(AnyPtr<const indexed_triangle_set> &&ptr);
void set_negative_mesh(const TriangleMesh &mesh);
void set_negative_mesh(const indexed_triangle_set &mesh);
void set_negative_mesh(AnyPtr<const indexed_triangle_set> &&ptr);
template<class It>
void set_mesh(const Range<It> &csgrange, bool copy_meshes = false)
{
if (! csg::is_same(range(m_csgmesh), csgrange)) {
m_csgmesh.clear();
if (copy_meshes)
csg::copy_csgrange_deep(csgrange, std::back_inserter(m_csgmesh));
else
csg::copy_csgrange_shallow(csgrange, std::back_inserter(m_csgmesh));
m_result.reset();
}
}
// Inform the MeshClipper about the transformation that transforms the mesh
// into world coordinates.
@ -92,34 +119,41 @@ public:
// Render the triangulated cut. Transformation matrices should
// be set in world coords.
void render_cut(const ColorRGBA& color);
void render_cut(const ColorRGBA& color, const std::vector<size_t>* ignore_idxs = nullptr);
void render_contour(const ColorRGBA& color, const std::vector<size_t>* ignore_idxs = nullptr);
bool is_projection_inside_cut(const Vec3d &point) const;
// Returns index of the contour which was clicked, -1 otherwise.
int is_projection_inside_cut(const Vec3d& point) const;
bool has_valid_contour() const;
int get_number_of_contours() const { return m_result ? m_result->cut_islands.size() : 0; }
std::vector<Vec3d> point_per_contour() const;
private:
void recalculate_triangles();
Geometry::Transformation m_trafo;
const TriangleMesh* m_mesh = nullptr;
const TriangleMesh* m_negative_mesh = nullptr;
AnyPtr<const indexed_triangle_set> m_mesh;
AnyPtr<const indexed_triangle_set> m_negative_mesh;
std::vector<csg::CSGPart> m_csgmesh;
ClippingPlane m_plane;
ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing();
std::vector<Vec2f> m_triangles2d;
GLModel m_model;
bool m_triangles_valid = false;
struct CutIsland
{
struct CutIsland {
GLModel model;
GLModel model_expanded;
ExPolygon expoly;
BoundingBox expoly_bb;
bool disabled = false;
size_t hash;
};
struct ClipResult
{
struct ClipResult {
std::vector<CutIsland> cut_islands;
Transform3d trafo; // this rotates the cut into world coords
};
std::optional<ClipResult> m_result; // the cut plane
std::optional<ClipResult> m_result;
bool m_fill_cut = true;
double m_contour_width = 0.;
};
@ -129,17 +163,20 @@ private:
class MeshRaycaster {
public:
explicit MeshRaycaster(std::shared_ptr<const TriangleMesh> mesh)
: m_mesh(mesh)
, m_emesh(*mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
, m_normals(its_face_normals(mesh->its))
: m_mesh(std::move(mesh))
, m_emesh(*m_mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
, m_normals(its_face_normals(m_mesh->its))
{
assert(m_mesh);
}
static void line_from_mouse_pos_static(const Vec2d &mouse_pos, const Transform3d &trafo,
const Camera &camera, Vec3d &point, Vec3d &direction);
explicit MeshRaycaster(const TriangleMesh &mesh)
: MeshRaycaster(std::make_unique<TriangleMesh>(mesh))
{}
void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3d& point, Vec3d& direction) const;
// DEPRICATED - use CameraUtils::ray_from_screen_pos
static void line_from_mouse_pos(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3d& point, Vec3d& direction);
// Given a mouse position, this returns true in case it is on the mesh.
bool unproject_on_mesh(
@ -153,6 +190,12 @@ public:
bool sinking_limit = true
) const;
const AABBMesh &get_aabb_mesh() const { return m_emesh; }
// Given a point and direction in world coords, returns whether the respective line
// intersects the mesh if it is transformed into world by trafo.
bool intersects_line(Vec3d point, Vec3d direction, const Transform3d& trafo) const;
// Given a vector of points in woorld coordinates, this returns vector
// of indices of points that are visible (i.e. not cut by clipping plane
// or obscured by part of the mesh.
@ -188,7 +231,7 @@ public:
private:
std::shared_ptr<const TriangleMesh> m_mesh;
sla::IndexedMesh m_emesh;
AABBMesh m_emesh;
std::vector<stl_normal> m_normals;
};