mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-08 07:27:41 -06:00
Sync most of the gizmos with latest PrusaSlicer
This commit is contained in:
parent
049dfd3e08
commit
1561d65712
33 changed files with 843 additions and 760 deletions
|
@ -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;
|
||||
|
|
|
@ -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 } });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
for (auto &grabber : m_grabbers) grabber.dragging = false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -788,14 +789,13 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
assert(m_cursor_type == TriangleSelector::CursorType::CIRCLE || m_cursor_type == TriangleSelector::CursorType::SPHERE);
|
||||
|
||||
if (projected_mouse_positions.size() == 1) {
|
||||
const ProjectedMousePosition& first_position = projected_mouse_positions.front();
|
||||
const ProjectedMousePosition &first_position = projected_mouse_positions.front();
|
||||
std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::SinglePointCursor::cursor_factory(first_position.mesh_hit,
|
||||
camera_pos, m_cursor_radius,
|
||||
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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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& selection = m_parent.get_selection();
|
||||
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()
|
||||
|
@ -388,9 +397,9 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data)
|
|||
}
|
||||
}
|
||||
|
||||
void GLGizmoScale3D::do_scale_uniform(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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
@ -298,8 +302,8 @@ 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);
|
||||
wxGetApp().CallAfter([this](){
|
||||
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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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_
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
@ -469,114 +388,26 @@ void ObjectClipper::set_position(double pos, bool keep_normal)
|
|||
get_pool()->get_canvas()->set_as_dirty();
|
||||
}
|
||||
|
||||
void ObjectClipper::set_range_and_pos(const Vec3d &cpl_normal, double cpl_offset, double pos)
|
||||
void ObjectClipper::set_range_and_pos(const Vec3d& cpl_normal, double cpl_offset, double pos)
|
||||
{
|
||||
m_clp.reset(new ClippingPlane(cpl_normal, cpl_offset));
|
||||
m_clp_ratio = pos;
|
||||
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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
void set_range_and_pos(const Vec3d &cpl_normal, double cpl_offset, double pos);
|
||||
int get_number_of_contours() const;
|
||||
std::vector<Vec3d> point_per_contour() const;
|
||||
|
||||
bool is_projection_inside_cut(const Vec3d &point_in) 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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,44 +156,94 @@ 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
|
||||
{
|
||||
return m_result && std::any_of(m_result->cut_islands.begin(), m_result->cut_islands.end(), [](const CutIsland &isl) { return !isl.expoly.empty(); });
|
||||
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()
|
|||
}
|
||||
}
|
||||
|
||||
for (const ExPolygon &exp : expolys) {
|
||||
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
|
||||
Transform3d tr2 = tr;
|
||||
tr2.pretranslate(0.002 * m_plane.get_normal().normalized());
|
||||
|
||||
m_model.reset();
|
||||
|
||||
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();
|
||||
|
||||
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,46 +416,26 @@ 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();
|
||||
point = inv * point;
|
||||
direction = inv.linear() * direction;
|
||||
point = inv*point;
|
||||
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>();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue