mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-18 22:31:13 -06:00
Measure: Initial porting of Measure Gizmo
This commit is contained in:
parent
1561d65712
commit
f72d42f920
31 changed files with 5276 additions and 146 deletions
|
@ -1,3 +1,12 @@
|
|||
#/|/ Copyright (c) Prusa Research 2018 - 2023 Tomáš Mészáros @tamasmeszaros, David Kocík @kocikdav, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, Filip Sykala @Jony01, Oleksandra Iushchenko @YuSanka, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral
|
||||
#/|/ Copyright (c) 2023 Pedro Lamas @PedroLamas
|
||||
#/|/ Copyright (c) 2020 Sergey Kovalev @RandoMan70
|
||||
#/|/ Copyright (c) 2021 Boleslaw Ciesielski
|
||||
#/|/ Copyright (c) 2019 Spencer Owen @spuder
|
||||
#/|/ Copyright (c) 2019 Stephan Reichhelm @stephanr
|
||||
#/|/
|
||||
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
#/|/
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(libslic3r_gui)
|
||||
|
||||
|
@ -135,6 +144,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/Gizmos/GLGizmoMmuSegmentation.hpp
|
||||
#GUI/Gizmos/GLGizmoFaceDetector.cpp
|
||||
#GUI/Gizmos/GLGizmoFaceDetector.hpp
|
||||
GUI/Gizmos/GLGizmoMeasure.cpp
|
||||
GUI/Gizmos/GLGizmoMeasure.hpp
|
||||
GUI/Gizmos/GLGizmoSeam.cpp
|
||||
GUI/Gizmos/GLGizmoSeam.hpp
|
||||
GUI/Gizmos/GLGizmoText.cpp
|
||||
|
@ -175,6 +186,8 @@ set(SLIC3R_GUI_SOURCES
|
|||
GUI/GUI_App.hpp
|
||||
GUI/GUI_Utils.cpp
|
||||
GUI/GUI_Utils.hpp
|
||||
GUI/GUI_Geometry.cpp
|
||||
GUI/GUI_Geometry.hpp
|
||||
GUI/I18N.cpp
|
||||
GUI/I18N.hpp
|
||||
GUI/MainFrame.cpp
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
///|/ Copyright (c) Prusa Research 2017 - 2023 Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, David Kocík @kocikdav, Vojtěch Král @vojtechkral
|
||||
///|/ Copyright (c) 2017 Eyal Soha @eyal0
|
||||
///|/ Copyright (c) Slic3r 2015 Alessandro Ranellucci @alranel
|
||||
///|/
|
||||
///|/ ported from lib/Slic3r/GUI/3DScene.pm:
|
||||
///|/ Copyright (c) Prusa Research 2016 - 2019 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka
|
||||
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
|
||||
///|/ Copyright (c) 2013 Guillaume Seguin @iXce
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_3DScene_hpp_
|
||||
#define slic3r_3DScene_hpp_
|
||||
|
||||
|
|
|
@ -2189,15 +2189,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
|
||||
_set_current();
|
||||
|
||||
m_hover_volume_idxs.clear();
|
||||
|
||||
GLGizmoBase* curr_gizmo = m_gizmos.get_current();
|
||||
if (curr_gizmo != nullptr)
|
||||
curr_gizmo->unregister_raycasters_for_picking();
|
||||
|
||||
struct ModelVolumeState {
|
||||
ModelVolumeState(const GLVolume* volume) :
|
||||
model_volume(nullptr), geometry_id(volume->geometry_id), volume_idx(-1) {}
|
||||
|
@ -2656,6 +2652,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
else
|
||||
m_selection.volumes_changed(map_glvolume_old_to_new);
|
||||
|
||||
// @Enrico suggest this solution to preven accessing pointer on caster without data
|
||||
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume);
|
||||
m_gizmos.update_data();
|
||||
m_gizmos.update_assemble_view_data();
|
||||
m_gizmos.refresh_on_off_state();
|
||||
|
@ -2663,9 +2661,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
// Update the toolbar
|
||||
//BBS: notify the PartPlateList to reload all objects
|
||||
if (update_object_list)
|
||||
{
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
|
||||
}
|
||||
|
||||
//BBS:exclude the assmble view
|
||||
if (m_canvas_type != ECanvasType::CanvasAssembleView) {
|
||||
|
@ -2711,14 +2707,19 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||
}
|
||||
|
||||
// refresh volume raycasters for picking
|
||||
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Volume);
|
||||
for (size_t i = 0; i < m_volumes.volumes.size(); ++i) {
|
||||
assert(m_volumes.volumes[i]->mesh_raycaster != nullptr);
|
||||
add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *m_volumes.volumes[i]->mesh_raycaster, m_volumes.volumes[i]->world_matrix());
|
||||
const GLVolume* v = m_volumes.volumes[i];
|
||||
assert(v->mesh_raycaster != nullptr);
|
||||
std::shared_ptr<SceneRaycasterItem> raycaster = add_raycaster_for_picking(SceneRaycaster::EType::Volume, i, *v->mesh_raycaster, v->world_matrix());
|
||||
raycaster->set_active(v->is_active);
|
||||
}
|
||||
|
||||
// refresh gizmo elements raycasters for picking
|
||||
GLGizmoBase* curr_gizmo = m_gizmos.get_current();
|
||||
if (curr_gizmo != nullptr)
|
||||
curr_gizmo->unregister_raycasters_for_picking();
|
||||
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::Gizmo);
|
||||
m_scene_raycaster.remove_raycasters(SceneRaycaster::EType::FallbackGizmo);
|
||||
if (curr_gizmo != nullptr && !m_selection.is_empty())
|
||||
curr_gizmo->register_raycasters_for_picking();
|
||||
|
||||
|
@ -6434,6 +6435,7 @@ void GLCanvas3D::_picking_pass()
|
|||
break;
|
||||
}
|
||||
case SceneRaycaster::EType::Gizmo:
|
||||
case SceneRaycaster::EType::FallbackGizmo:
|
||||
{
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
const bool inside = 0 <= m_mouse.position.x() && m_mouse.position.x() < cnv_size.get_width() &&
|
||||
|
@ -6474,42 +6476,94 @@ void GLCanvas3D::_picking_pass()
|
|||
{
|
||||
case SceneRaycaster::EType::Bed: { object_type = "Bed"; break; }
|
||||
case SceneRaycaster::EType::Gizmo: { object_type = "Gizmo element"; break; }
|
||||
case SceneRaycaster::EType::FallbackGizmo: { object_type = "Gizmo2 element"; break; }
|
||||
case SceneRaycaster::EType::Volume:
|
||||
{
|
||||
if (m_volumes.volumes[hit.raycaster_id]->is_wipe_tower)
|
||||
object_type = "Wipe tower";
|
||||
object_type = "Volume (Wipe tower)";
|
||||
else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposPad))
|
||||
object_type = "SLA pad";
|
||||
object_type = "Volume (SLA pad)";
|
||||
else if (m_volumes.volumes[hit.raycaster_id]->volume_idx() == -int(slaposSupportTree))
|
||||
object_type = "SLA supports";
|
||||
object_type = "Volume (SLA supports)";
|
||||
else if (m_volumes.volumes[hit.raycaster_id]->is_modifier)
|
||||
object_type = "Volume (Modifier)";
|
||||
else
|
||||
object_type = "Volume";
|
||||
object_type = "Volume (Part)";
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
}
|
||||
|
||||
auto add_strings_row_to_table = [&imgui](const std::string& col_1, const ImVec4& col_1_color, const std::string& col_2, const ImVec4& col_2_color,
|
||||
const std::string& col_3 = "", const ImVec4& col_3_color = ImGui::GetStyleColorVec4(ImGuiCol_Text)) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
imgui.text_colored(col_1_color, col_1.c_str());
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
imgui.text_colored(col_2_color, col_2.c_str());
|
||||
if (!col_3.empty()) {
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
imgui.text_colored(col_3_color, col_3.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
char buf[1024];
|
||||
if (hit.type != SceneRaycaster::EType::None) {
|
||||
sprintf(buf, "Object ID: %d", hit.raycaster_id);
|
||||
imgui.text(std::string(buf));
|
||||
sprintf(buf, "Type: %s", object_type.c_str());
|
||||
imgui.text(std::string(buf));
|
||||
sprintf(buf, "Position: %.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z());
|
||||
imgui.text(std::string(buf));
|
||||
sprintf(buf, "Normal: %.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z());
|
||||
imgui.text(std::string(buf));
|
||||
if (ImGui::BeginTable("Hit", 2)) {
|
||||
add_strings_row_to_table("Object ID", ImGuiWrapper::COL_ORANGE_LIGHT, std::to_string(hit.raycaster_id), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
add_strings_row_to_table("Type", ImGuiWrapper::COL_ORANGE_LIGHT, object_type, ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
sprintf(buf, "%.3f, %.3f, %.3f", hit.position.x(), hit.position.y(), hit.position.z());
|
||||
add_strings_row_to_table("Position", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
sprintf(buf, "%.3f, %.3f, %.3f", hit.normal.x(), hit.normal.y(), hit.normal.z());
|
||||
add_strings_row_to_table("Normal", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
else
|
||||
imgui.text("NO HIT");
|
||||
|
||||
ImGui::Separator();
|
||||
imgui.text("Registered for picking:");
|
||||
sprintf(buf, "Beds: %d", (int)m_scene_raycaster.beds_count());
|
||||
imgui.text(std::string(buf));
|
||||
sprintf(buf, "Volumes: %d", (int)m_scene_raycaster.volumes_count());
|
||||
imgui.text(std::string(buf));
|
||||
sprintf(buf, "Gizmo elements: %d", (int)m_scene_raycaster.gizmos_count());
|
||||
imgui.text(std::string(buf));
|
||||
if (ImGui::BeginTable("Raycasters", 2)) {
|
||||
sprintf(buf, "%d (%d)", (int)m_scene_raycaster.beds_count(), (int)m_scene_raycaster.active_beds_count());
|
||||
add_strings_row_to_table("Beds", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
sprintf(buf, "%d (%d)", (int)m_scene_raycaster.volumes_count(), (int)m_scene_raycaster.active_volumes_count());
|
||||
add_strings_row_to_table("Volumes", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
sprintf(buf, "%d (%d)", (int)m_scene_raycaster.gizmos_count(), (int)m_scene_raycaster.active_gizmos_count());
|
||||
add_strings_row_to_table("Gizmo elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
sprintf(buf, "%d (%d)", (int)m_scene_raycaster.fallback_gizmos_count(), (int)m_scene_raycaster.active_fallback_gizmos_count());
|
||||
add_strings_row_to_table("Gizmo2 elements", ImGuiWrapper::COL_ORANGE_LIGHT, std::string(buf), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>>* gizmo_raycasters = m_scene_raycaster.get_raycasters(SceneRaycaster::EType::Gizmo);
|
||||
if (gizmo_raycasters != nullptr && !gizmo_raycasters->empty()) {
|
||||
ImGui::Separator();
|
||||
imgui.text("Gizmo raycasters IDs:");
|
||||
if (ImGui::BeginTable("GizmoRaycasters", 3)) {
|
||||
for (size_t i = 0; i < gizmo_raycasters->size(); ++i) {
|
||||
add_strings_row_to_table(std::to_string(i), ImGuiWrapper::COL_ORANGE_LIGHT,
|
||||
std::to_string(SceneRaycaster::decode_id(SceneRaycaster::EType::Gizmo, (*gizmo_raycasters)[i]->get_id())), ImGui::GetStyleColorVec4(ImGuiCol_Text),
|
||||
to_string(Geometry::Transformation((*gizmo_raycasters)[i]->get_transform()).get_offset()), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>>* gizmo2_raycasters = m_scene_raycaster.get_raycasters(SceneRaycaster::EType::FallbackGizmo);
|
||||
if (gizmo2_raycasters != nullptr && !gizmo2_raycasters->empty()) {
|
||||
ImGui::Separator();
|
||||
imgui.text("Gizmo2 raycasters IDs:");
|
||||
if (ImGui::BeginTable("Gizmo2Raycasters", 3)) {
|
||||
for (size_t i = 0; i < gizmo2_raycasters->size(); ++i) {
|
||||
add_strings_row_to_table(std::to_string(i), ImGuiWrapper::COL_ORANGE_LIGHT,
|
||||
std::to_string(SceneRaycaster::decode_id(SceneRaycaster::EType::FallbackGizmo, (*gizmo2_raycasters)[i]->get_id())), ImGui::GetStyleColorVec4(ImGuiCol_Text),
|
||||
to_string(Geometry::Transformation((*gizmo2_raycasters)[i]->get_transform()).get_offset()), ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
imgui.end();
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
///|/ Copyright (c) Prusa Research 2018 - 2023 Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Král @vojtechkral
|
||||
///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GLCanvas3D_hpp_
|
||||
#define slic3r_GLCanvas3D_hpp_
|
||||
|
||||
|
@ -724,9 +729,9 @@ public:
|
|||
|
||||
bool init();
|
||||
void post_event(wxEvent &&event);
|
||||
|
||||
|
||||
std::shared_ptr<SceneRaycasterItem> add_raycaster_for_picking(SceneRaycaster::EType type, int id, const MeshRaycaster& raycaster,
|
||||
const Transform3d& trafo, bool use_back_faces = false) {
|
||||
const Transform3d& trafo = Transform3d::Identity(), bool use_back_faces = false) {
|
||||
return m_scene_raycaster.add_raycaster(type, id, raycaster, trafo, use_back_faces);
|
||||
}
|
||||
void remove_raycasters_for_picking(SceneRaycaster::EType type, int id) {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2023 Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "GLModel.hpp"
|
||||
|
||||
|
@ -247,6 +251,21 @@ void GLModel::Geometry::remove_vertex(size_t id)
|
|||
}
|
||||
}
|
||||
|
||||
indexed_triangle_set GLModel::Geometry::get_as_indexed_triangle_set() const
|
||||
{
|
||||
indexed_triangle_set its;
|
||||
its.vertices.reserve(vertices_count());
|
||||
for (size_t i = 0; i < vertices_count(); ++i) {
|
||||
its.vertices.emplace_back(extract_position_3(i));
|
||||
}
|
||||
its.indices.reserve(indices_count() / 3);
|
||||
for (size_t i = 0; i < indices_count() / 3; ++i) {
|
||||
const size_t tri_id = i * 3;
|
||||
its.indices.emplace_back(extract_index(tri_id), extract_index(tri_id + 1), extract_index(tri_id + 2));
|
||||
}
|
||||
return its;
|
||||
}
|
||||
|
||||
size_t GLModel::Geometry::vertex_stride_floats(const Format& format)
|
||||
{
|
||||
switch (format.vertex_layout)
|
||||
|
@ -1212,5 +1231,190 @@ GLModel::Geometry diamond(unsigned int resolution)
|
|||
return data;
|
||||
}
|
||||
|
||||
GLModel::Geometry smooth_sphere(unsigned int resolution, float radius)
|
||||
{
|
||||
resolution = std::max<unsigned int>(4, resolution);
|
||||
|
||||
const unsigned int sectorCount = resolution;
|
||||
const unsigned int stackCount = resolution;
|
||||
|
||||
const float sectorStep = float(2.0 * M_PI / sectorCount);
|
||||
const float stackStep = float(M_PI / stackCount);
|
||||
|
||||
GLModel::Geometry data;
|
||||
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
||||
data.reserve_vertices((stackCount - 1) * sectorCount + 2);
|
||||
data.reserve_indices((2 * (stackCount - 1) * sectorCount) * 3);
|
||||
|
||||
// vertices
|
||||
for (unsigned int i = 0; i <= stackCount; ++i) {
|
||||
// from pi/2 to -pi/2
|
||||
const double stackAngle = 0.5 * M_PI - stackStep * i;
|
||||
const double xy = double(radius) * ::cos(stackAngle);
|
||||
const double z = double(radius) * ::sin(stackAngle);
|
||||
if (i == 0 || i == stackCount) {
|
||||
const Vec3f v(float(xy), 0.0f, float(z));
|
||||
data.add_vertex(v, (Vec3f)v.normalized());
|
||||
}
|
||||
else {
|
||||
for (unsigned int j = 0; j < sectorCount; ++j) {
|
||||
// from 0 to 2pi
|
||||
const double sectorAngle = sectorStep * j;
|
||||
const Vec3f v(float(xy * std::cos(sectorAngle)), float(xy * std::sin(sectorAngle)), float(z));
|
||||
data.add_vertex(v, (Vec3f)v.normalized());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// triangles
|
||||
for (unsigned int i = 0; i < stackCount; ++i) {
|
||||
// Beginning of current stack.
|
||||
unsigned int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
|
||||
const unsigned int k1_first = k1;
|
||||
// Beginning of next stack.
|
||||
unsigned int k2 = (i == 0) ? 1 : (k1 + sectorCount);
|
||||
const unsigned int k2_first = k2;
|
||||
for (unsigned int j = 0; j < sectorCount; ++j) {
|
||||
// 2 triangles per sector excluding first and last stacks
|
||||
unsigned int k1_next = k1;
|
||||
unsigned int k2_next = k2;
|
||||
if (i != 0) {
|
||||
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
|
||||
data.add_triangle(k1, k2, k1_next);
|
||||
}
|
||||
if (i + 1 != stackCount) {
|
||||
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
|
||||
data.add_triangle(k1_next, k2, k2_next);
|
||||
}
|
||||
k1 = k1_next;
|
||||
k2 = k2_next;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height)
|
||||
{
|
||||
resolution = std::max<unsigned int>(4, resolution);
|
||||
|
||||
const unsigned int sectorCount = resolution;
|
||||
const float sectorStep = 2.0f * float(M_PI) / float(sectorCount);
|
||||
|
||||
GLModel::Geometry data;
|
||||
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
||||
data.reserve_vertices(sectorCount * 4 + 2);
|
||||
data.reserve_indices(sectorCount * 4 * 3);
|
||||
|
||||
auto generate_vertices_on_circle = [sectorCount, sectorStep](float radius) {
|
||||
std::vector<Vec3f> ret;
|
||||
ret.reserve(sectorCount);
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
// from 0 to 2pi
|
||||
const float sectorAngle = sectorStep * i;
|
||||
ret.emplace_back(radius * std::cos(sectorAngle), radius * std::sin(sectorAngle), 0.0f);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
const std::vector<Vec3f> base_vertices = generate_vertices_on_circle(radius);
|
||||
const Vec3f h = height * Vec3f::UnitZ();
|
||||
|
||||
// stem vertices
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
const Vec3f& v = base_vertices[i];
|
||||
const Vec3f n = v.normalized();
|
||||
data.add_vertex(v, n);
|
||||
data.add_vertex(v + h, n);
|
||||
}
|
||||
|
||||
// stem triangles
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
unsigned int v1 = i * 2;
|
||||
unsigned int v2 = (i < sectorCount - 1) ? v1 + 2 : 0;
|
||||
unsigned int v3 = v2 + 1;
|
||||
unsigned int v4 = v1 + 1;
|
||||
data.add_triangle(v1, v2, v3);
|
||||
data.add_triangle(v1, v3, v4);
|
||||
}
|
||||
|
||||
// bottom cap vertices
|
||||
Vec3f cap_center = Vec3f::Zero();
|
||||
unsigned int cap_center_id = data.vertices_count();
|
||||
Vec3f normal = -Vec3f::UnitZ();
|
||||
|
||||
data.add_vertex(cap_center, normal);
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
data.add_vertex(base_vertices[i], normal);
|
||||
}
|
||||
|
||||
// bottom cap triangles
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
data.add_triangle(cap_center_id, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1, cap_center_id + i + 1);
|
||||
}
|
||||
|
||||
// top cap vertices
|
||||
cap_center += h;
|
||||
cap_center_id = data.vertices_count();
|
||||
normal = -normal;
|
||||
|
||||
data.add_vertex(cap_center, normal);
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
data.add_vertex(base_vertices[i] + h, normal);
|
||||
}
|
||||
|
||||
// top cap triangles
|
||||
for (unsigned int i = 0; i < sectorCount; ++i) {
|
||||
data.add_triangle(cap_center_id, cap_center_id + i + 1, (i < sectorCount - 1) ? cap_center_id + i + 2 : cap_center_id + 1);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness)
|
||||
{
|
||||
const unsigned int torus_sector_count = std::max<unsigned int>(4, primary_resolution);
|
||||
const float torus_sector_step = 2.0f * float(M_PI) / float(torus_sector_count);
|
||||
const unsigned int section_sector_count = std::max<unsigned int>(4, secondary_resolution);
|
||||
const float section_sector_step = 2.0f * float(M_PI) / float(section_sector_count);
|
||||
|
||||
GLModel::Geometry data;
|
||||
data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3N3 };
|
||||
data.reserve_vertices(torus_sector_count * section_sector_count);
|
||||
data.reserve_indices(torus_sector_count * section_sector_count * 2 * 3);
|
||||
|
||||
// vertices
|
||||
for (unsigned int i = 0; i < torus_sector_count; ++i) {
|
||||
const float section_angle = torus_sector_step * i;
|
||||
const float csa = std::cos(section_angle);
|
||||
const float ssa = std::sin(section_angle);
|
||||
const Vec3f section_center(radius * csa, radius * ssa, 0.0f);
|
||||
for (unsigned int j = 0; j < section_sector_count; ++j) {
|
||||
const float circle_angle = section_sector_step * j;
|
||||
const float thickness_xy = thickness * std::cos(circle_angle);
|
||||
const float thickness_z = thickness * std::sin(circle_angle);
|
||||
const Vec3f v(thickness_xy * csa, thickness_xy * ssa, thickness_z);
|
||||
data.add_vertex(section_center + v, (Vec3f)v.normalized());
|
||||
}
|
||||
}
|
||||
|
||||
// triangles
|
||||
for (unsigned int i = 0; i < torus_sector_count; ++i) {
|
||||
const unsigned int ii = i * section_sector_count;
|
||||
const unsigned int ii_next = ((i + 1) % torus_sector_count) * section_sector_count;
|
||||
for (unsigned int j = 0; j < section_sector_count; ++j) {
|
||||
const unsigned int j_next = (j + 1) % section_sector_count;
|
||||
const unsigned int i0 = ii + j;
|
||||
const unsigned int i1 = ii_next + j;
|
||||
const unsigned int i2 = ii_next + j_next;
|
||||
const unsigned int i3 = ii + j_next;
|
||||
data.add_triangle(i0, i1, i2);
|
||||
data.add_triangle(i0, i2, i3);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2020 - 2023 Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GLModel_hpp_
|
||||
#define slic3r_GLModel_hpp_
|
||||
|
||||
|
@ -101,6 +105,8 @@ namespace GUI {
|
|||
size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); }
|
||||
size_t indices_size_bytes() const { return indices.size() * index_stride_bytes(*this); }
|
||||
|
||||
indexed_triangle_set get_as_indexed_triangle_set() const;
|
||||
|
||||
static size_t vertex_stride_floats(const Format& format);
|
||||
static size_t vertex_stride_bytes(const Format& format) { return vertex_stride_floats(format) * sizeof(float); }
|
||||
|
||||
|
@ -233,6 +239,18 @@ namespace GUI {
|
|||
// the diamond is contained into a box with size [1, 1, 1]
|
||||
GLModel::Geometry diamond(unsigned int resolution);
|
||||
|
||||
// create a sphere with smooth normals
|
||||
// the origin of the sphere is in its center
|
||||
GLModel::Geometry smooth_sphere(unsigned int resolution, float radius);
|
||||
// create a cylinder with smooth normals
|
||||
// the axis of the cylinder is the Z axis
|
||||
// the origin of the cylinder is the center of its bottom cap face
|
||||
GLModel::Geometry smooth_cylinder(unsigned int resolution, float radius, float height);
|
||||
// create a torus with smooth normals
|
||||
// the axis of the torus is the Z axis
|
||||
// the origin of the torus is in its center
|
||||
GLModel::Geometry smooth_torus(unsigned int primary_resolution, unsigned int secondary_resolution, float radius, float thickness);
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
13
src/slic3r/GUI/GUI_Geometry.cpp
Normal file
13
src/slic3r/GUI/GUI_Geometry.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 Enrico Turri @enricoturri1966
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "GUI_Geometry.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace GUI
|
82
src/slic3r/GUI/GUI_Geometry.hpp
Normal file
82
src/slic3r/GUI/GUI_Geometry.hpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
///|/ Copyright (c) Prusa Research 2021 - 2023 Enrico Turri @enricoturri1966
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GUI_Geometry_hpp_
|
||||
#define slic3r_GUI_Geometry_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
enum class ECoordinatesType : unsigned char
|
||||
{
|
||||
World,
|
||||
Instance,
|
||||
Local
|
||||
};
|
||||
|
||||
class TransformationType
|
||||
{
|
||||
public:
|
||||
enum Enum {
|
||||
// Transforming in a world coordinate system
|
||||
World = 0,
|
||||
// Transforming in a instance coordinate system
|
||||
Instance = 1,
|
||||
// Transforming in a local coordinate system
|
||||
Local = 2,
|
||||
// Absolute transformations, allowed in local coordinate system only.
|
||||
Absolute = 0,
|
||||
// Relative transformations, allowed in both local and world coordinate system.
|
||||
Relative = 4,
|
||||
// For group selection, the transformation is performed as if the group made a single solid body.
|
||||
Joint = 0,
|
||||
// For group selection, the transformation is performed on each object independently.
|
||||
Independent = 8,
|
||||
|
||||
World_Relative_Joint = World | Relative | Joint,
|
||||
World_Relative_Independent = World | Relative | Independent,
|
||||
Instance_Absolute_Joint = Instance | Absolute | Joint,
|
||||
Instance_Absolute_Independent = Instance | Absolute | Independent,
|
||||
Instance_Relative_Joint = Instance | Relative | Joint,
|
||||
Instance_Relative_Independent = Instance | Relative | Independent,
|
||||
Local_Absolute_Joint = Local | Absolute | Joint,
|
||||
Local_Absolute_Independent = Local | Absolute | Independent,
|
||||
Local_Relative_Joint = Local | Relative | Joint,
|
||||
Local_Relative_Independent = Local | Relative | Independent,
|
||||
};
|
||||
|
||||
TransformationType() : m_value(World) {}
|
||||
TransformationType(Enum value) : m_value(value) {}
|
||||
TransformationType& operator=(Enum value) { m_value = value; return *this; }
|
||||
|
||||
Enum operator()() const { return m_value; }
|
||||
bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; }
|
||||
|
||||
void set_world() { this->remove(Instance); this->remove(Local); }
|
||||
void set_instance() { this->remove(Local); this->add(Instance); }
|
||||
void set_local() { this->remove(Instance); this->add(Local); }
|
||||
void set_absolute() { this->remove(Relative); }
|
||||
void set_relative() { this->add(Relative); }
|
||||
void set_joint() { this->remove(Independent); }
|
||||
void set_independent() { this->add(Independent); }
|
||||
|
||||
bool world() const { return !this->has(Instance) && !this->has(Local); }
|
||||
bool instance() const { return this->has(Instance); }
|
||||
bool local() const { return this->has(Local); }
|
||||
bool absolute() const { return !this->has(Relative); }
|
||||
bool relative() const { return this->has(Relative); }
|
||||
bool joint() const { return !this->has(Independent); }
|
||||
bool independent() const { return this->has(Independent); }
|
||||
|
||||
private:
|
||||
void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); }
|
||||
void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); }
|
||||
|
||||
Enum m_value;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace GUI
|
||||
|
||||
#endif // slic3r_GUI_Geometry_hpp_
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Vojtěch Král @vojtechkral
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GUI_Utils_hpp_
|
||||
#define slic3r_GUI_Utils_hpp_
|
||||
|
||||
|
@ -463,6 +467,16 @@ public:
|
|||
~TaskTimer();
|
||||
};
|
||||
|
||||
class KeyAutoRepeatFilter
|
||||
{
|
||||
size_t m_count{ 0 };
|
||||
|
||||
public:
|
||||
void increase_count() { ++m_count; }
|
||||
void reset_count() { m_count = 0; }
|
||||
bool is_first() const { return m_count == 0; }
|
||||
};
|
||||
|
||||
|
||||
/* Image Generator */
|
||||
#define _3MF_COVER_SIZE wxSize(240, 240)
|
||||
|
|
2154
src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
Normal file
2154
src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
Normal file
File diff suppressed because it is too large
Load diff
190
src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp
Normal file
190
src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp
Normal file
|
@ -0,0 +1,190 @@
|
|||
///|/ Copyright (c) Prusa Research 2019 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Filip Sykala @Jony01
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GLGizmoMeasure_hpp_
|
||||
#define slic3r_GLGizmoMeasure_hpp_
|
||||
|
||||
#include "GLGizmoBase.hpp"
|
||||
#include "slic3r/GUI/GLModel.hpp"
|
||||
#include "slic3r/GUI/GUI_Utils.hpp"
|
||||
#include "slic3r/GUI/MeshUtils.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "libslic3r/Measure.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class ModelVolumeType : int;
|
||||
|
||||
namespace Measure { class Measuring; }
|
||||
|
||||
|
||||
namespace GUI {
|
||||
|
||||
enum class SLAGizmoEventType : unsigned char;
|
||||
|
||||
class GLGizmoMeasure : public GLGizmoBase
|
||||
{
|
||||
enum class EMode : unsigned char
|
||||
{
|
||||
FeatureSelection,
|
||||
PointSelection
|
||||
};
|
||||
|
||||
struct SelectedFeatures
|
||||
{
|
||||
struct Item
|
||||
{
|
||||
bool is_center{ false };
|
||||
std::optional<Measure::SurfaceFeature> source;
|
||||
std::optional<Measure::SurfaceFeature> feature;
|
||||
|
||||
bool operator == (const Item& other) const {
|
||||
return this->is_center == other.is_center && this->source == other.source && this->feature == other.feature;
|
||||
}
|
||||
|
||||
bool operator != (const Item& other) const {
|
||||
return !operator == (other);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
is_center = false;
|
||||
source.reset();
|
||||
feature.reset();
|
||||
}
|
||||
};
|
||||
|
||||
Item first;
|
||||
Item second;
|
||||
|
||||
void reset() {
|
||||
first.reset();
|
||||
second.reset();
|
||||
}
|
||||
|
||||
bool operator == (const SelectedFeatures & other) const {
|
||||
if (this->first != other.first) return false;
|
||||
return this->second == other.second;
|
||||
}
|
||||
|
||||
bool operator != (const SelectedFeatures & other) const {
|
||||
return !operator == (other);
|
||||
}
|
||||
};
|
||||
|
||||
struct VolumeCacheItem
|
||||
{
|
||||
const ModelObject* object{ nullptr };
|
||||
const ModelInstance* instance{ nullptr };
|
||||
const ModelVolume* volume{ nullptr };
|
||||
Transform3d world_trafo;
|
||||
|
||||
bool operator == (const VolumeCacheItem& other) const {
|
||||
return this->object == other.object && this->instance == other.instance && this->volume == other.volume &&
|
||||
this->world_trafo.isApprox(other.world_trafo);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<VolumeCacheItem> m_volumes_cache;
|
||||
|
||||
EMode m_mode{ EMode::FeatureSelection };
|
||||
Measure::MeasurementResult m_measurement_result;
|
||||
|
||||
std::unique_ptr<Measure::Measuring> m_measuring; // PIMPL
|
||||
|
||||
PickingModel m_sphere;
|
||||
PickingModel m_cylinder;
|
||||
PickingModel m_circle;
|
||||
PickingModel m_plane;
|
||||
struct Dimensioning
|
||||
{
|
||||
GLModel line;
|
||||
GLModel triangle;
|
||||
GLModel arc;
|
||||
};
|
||||
Dimensioning m_dimensioning;
|
||||
|
||||
// Uses a standalone raycaster and not the shared one because of the
|
||||
// difference in how the mesh is updated
|
||||
std::unique_ptr<MeshRaycaster> m_raycaster;
|
||||
|
||||
std::vector<GLModel> m_plane_models_cache;
|
||||
std::map<int, std::shared_ptr<SceneRaycasterItem>> m_raycasters;
|
||||
// used to keep the raycasters for point/center spheres
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_selected_sphere_raycasters;
|
||||
std::optional<Measure::SurfaceFeature> m_curr_feature;
|
||||
std::optional<Vec3d> m_curr_point_on_feature_position;
|
||||
struct SceneRaycasterState
|
||||
{
|
||||
std::shared_ptr<SceneRaycasterItem> raycaster{ nullptr };
|
||||
bool state{true};
|
||||
|
||||
};
|
||||
std::vector<SceneRaycasterState> m_scene_raycasters;
|
||||
|
||||
// These hold information to decide whether recalculation is necessary:
|
||||
float m_last_inv_zoom{ 0.0f };
|
||||
std::optional<Measure::SurfaceFeature> m_last_circle;
|
||||
int m_last_plane_idx{ -1 };
|
||||
|
||||
bool m_mouse_left_down{ false }; // for detection left_up of this gizmo
|
||||
|
||||
Vec2d m_mouse_pos{ Vec2d::Zero() };
|
||||
|
||||
KeyAutoRepeatFilter m_shift_kar_filter;
|
||||
|
||||
SelectedFeatures m_selected_features;
|
||||
bool m_pending_scale{ false };
|
||||
bool m_editing_distance{ false };
|
||||
bool m_is_editing_distance_first_frame{ true };
|
||||
|
||||
void update_if_needed();
|
||||
|
||||
void disable_scene_raycasters();
|
||||
void restore_scene_raycasters_state();
|
||||
|
||||
void render_dimensioning();
|
||||
|
||||
#if ENABLE_MEASURE_GIZMO_DEBUG
|
||||
void render_debug_dialog();
|
||||
#endif // ENABLE_MEASURE_GIZMO_DEBUG
|
||||
|
||||
public:
|
||||
GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
|
||||
|
||||
/// <summary>
|
||||
/// Apply rotation on select plane
|
||||
/// </summary>
|
||||
/// <param name="mouse_event">Keep information about mouse click</param>
|
||||
/// <returns>Return True when use the information otherwise False.</returns>
|
||||
bool on_mouse(const wxMouseEvent &mouse_event) override;
|
||||
|
||||
void data_changed(bool is_serializing) override;
|
||||
|
||||
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
|
||||
|
||||
bool wants_enter_leave_snapshots() const override { return true; }
|
||||
std::string get_gizmo_entering_text() const override { return _u8L("Entering Measure gizmo"); }
|
||||
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Measure gizmo"); }
|
||||
std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); }
|
||||
|
||||
protected:
|
||||
bool on_init() override;
|
||||
std::string on_get_name() const override;
|
||||
bool on_is_activable() const override;
|
||||
void on_render() override;
|
||||
void on_set_state() override;
|
||||
|
||||
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
|
||||
virtual void on_register_raycasters_for_picking() override;
|
||||
virtual void on_unregister_raycasters_for_picking() override;
|
||||
|
||||
void remove_selected_sphere_raycaster(int id);
|
||||
void update_measurement_result();
|
||||
};
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_GLGizmoMeasure_hpp_
|
|
@ -30,8 +30,12 @@ enum class SLAGizmoEventType : unsigned char {
|
|||
Dragging,
|
||||
Delete,
|
||||
SelectAll,
|
||||
CtrlDown,
|
||||
CtrlUp,
|
||||
ShiftDown,
|
||||
ShiftUp,
|
||||
AltUp,
|
||||
Escape,
|
||||
ApplyChanges,
|
||||
DiscardChanges,
|
||||
AutomaticGeneration,
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMeasure.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoText.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp"
|
||||
|
||||
|
@ -195,6 +196,7 @@ bool GLGizmosManager::init()
|
|||
m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam));
|
||||
m_gizmos.emplace_back(new GLGizmoText(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Text));
|
||||
m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg", EType::MmuSegmentation));
|
||||
m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, "measure.svg", EType::Measure));
|
||||
m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify));
|
||||
//m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++));
|
||||
//m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++));
|
||||
|
@ -438,6 +440,8 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p
|
|||
return dynamic_cast<GLGizmoMmuSegmentation*>(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Text)
|
||||
return dynamic_cast<GLGizmoText*>(m_gizmos[Text].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Measure)
|
||||
return dynamic_cast<GLGizmoMeasure*>(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == Cut)
|
||||
return dynamic_cast<GLGizmoAdvancedCut *>(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down);
|
||||
else if (m_current == MeshBoolean)
|
||||
|
@ -689,8 +693,10 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
|
|||
// key ESC
|
||||
case WXK_ESCAPE:
|
||||
{
|
||||
if (m_current != Undefined)
|
||||
{
|
||||
if (m_current != Undefined) {
|
||||
if (m_current == Measure && gizmo_event(SLAGizmoEventType::Escape)) {
|
||||
// do nothing
|
||||
} else
|
||||
//if ((m_current != SlaSupports) || !gizmo_event(SLAGizmoEventType::DiscardChanges))
|
||||
reset_all_states();
|
||||
|
||||
|
@ -698,7 +704,14 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt)
|
|||
}
|
||||
break;
|
||||
}
|
||||
//skip some keys when gizmo
|
||||
case WXK_BACK:
|
||||
case WXK_DELETE:
|
||||
{
|
||||
if ((m_current == Cut || m_current == Measure) && gizmo_event(SLAGizmoEventType::Delete))
|
||||
processed = true;
|
||||
|
||||
break;
|
||||
}
|
||||
case 'A':
|
||||
case 'a':
|
||||
{
|
||||
|
@ -827,6 +840,12 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
|||
processed = true;
|
||||
}
|
||||
}*/
|
||||
if (m_current == Measure) {
|
||||
if (keyCode == WXK_CONTROL)
|
||||
gizmo_event(SLAGizmoEventType::CtrlUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
|
||||
else if (keyCode == WXK_SHIFT)
|
||||
gizmo_event(SLAGizmoEventType::ShiftUp, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
|
||||
}
|
||||
|
||||
// if (processed)
|
||||
// m_parent.set_cursor(GLCanvas3D::Standard);
|
||||
|
@ -897,6 +916,12 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt)
|
|||
wxGetApp().imgui()->set_requires_extra_frame();
|
||||
}
|
||||
}
|
||||
else if (m_current == Measure) {
|
||||
if (keyCode == WXK_CONTROL)
|
||||
gizmo_event(SLAGizmoEventType::CtrlDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
|
||||
else if (keyCode == WXK_SHIFT)
|
||||
gizmo_event(SLAGizmoEventType::ShiftDown, Vec2d::Zero(), evt.ShiftDown(), evt.AltDown(), evt.CmdDown());
|
||||
}
|
||||
}
|
||||
|
||||
if (processed)
|
||||
|
|
|
@ -88,6 +88,7 @@ public:
|
|||
// BBS
|
||||
Text,
|
||||
MmuSegmentation,
|
||||
Measure,
|
||||
Simplify,
|
||||
//SlaSupports,
|
||||
// BBS
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Lukáš Matěna @lukasmatena, Enrico Turri @enricoturri1966, David Kocík @kocikdav, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Filip Sykala @Jony01, Lukáš Hejl @hejllukas, Vojtěch Král @vojtechkral
|
||||
///|/ Copyright (c) 2019 Jason Tibbitts @jasontibbitts
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "ImGuiWrapper.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
@ -63,6 +68,7 @@ static const std::map<const wchar_t, std::string> font_icons = {
|
|||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
{ImGui::SliderFloatEditBtnIcon, "edit_button" },
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
{ImGui::ClipboardBtnIcon , "copy_menu" },
|
||||
{ImGui::CircleButtonIcon , "circle_paint" },
|
||||
{ImGui::TriangleButtonIcon , "triangle_paint" },
|
||||
{ImGui::FillButtonIcon , "fill_paint" },
|
||||
|
@ -789,11 +795,6 @@ bool ImGuiWrapper::radio_button(const wxString &label, bool active)
|
|||
return ImGui::RadioButton(label_utf8.c_str(), active);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::image_button()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
|
||||
{
|
||||
return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal);
|
||||
|
@ -1035,6 +1036,71 @@ bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, fl
|
|||
}
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
static bool image_button_ex(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2);
|
||||
ImGui::ItemSize(bb);
|
||||
if (!ImGui::ItemAdd(bb, id))
|
||||
return false;
|
||||
|
||||
bool hovered, held;
|
||||
bool pressed = ImGui::ButtonBehavior(bb, id, &hovered, &held, flags);
|
||||
|
||||
// Render
|
||||
const ImU32 col = ImGui::GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
|
||||
ImGui::RenderNavHighlight(bb, id);
|
||||
ImGui::RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, g.Style.FrameRounding));
|
||||
if (bg_col.w > 0.0f)
|
||||
window->DrawList->AddRectFilled(bb.Min + padding, bb.Max - padding, ImGui::GetColorU32(bg_col));
|
||||
window->DrawList->AddImage(texture_id, bb.Min + padding, bb.Max - padding, uv0, uv1, ImGui::GetColorU32(tint_col));
|
||||
|
||||
return pressed;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col, ImGuiButtonFlags flags)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
// Default to using texture ID as ID. User can still push string/integer prefixes.
|
||||
ImGui::PushID((void*)(intptr_t)user_texture_id);
|
||||
const ImGuiID id = window->GetID("#image");
|
||||
ImGui::PopID();
|
||||
|
||||
const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : g.Style.FramePadding;
|
||||
return image_button_ex(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col, flags);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::image_button(const wchar_t icon, const wxString& tooltip)
|
||||
{
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
const ImTextureID tex_id = io.Fonts->TexID;
|
||||
assert(io.Fonts->TexWidth > 0 && io.Fonts->TexHeight > 0);
|
||||
const float inv_tex_w = 1.0f / float(io.Fonts->TexWidth);
|
||||
const float inv_tex_h = 1.0f / float(io.Fonts->TexHeight);
|
||||
const ImFontAtlasCustomRect* const rect = GetTextureCustomRect(icon);
|
||||
const ImVec2 size = { float(rect->Width), float(rect->Height) };
|
||||
const ImVec2 uv0 = ImVec2(float(rect->X) * inv_tex_w, float(rect->Y) * inv_tex_h);
|
||||
const ImVec2 uv1 = ImVec2(float(rect->X + rect->Width) * inv_tex_w, float(rect->Y + rect->Height) * inv_tex_h);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, { 0.25f, 0.25f, 0.25f, 0.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.4f, 0.4f, 0.4f, 1.0f });
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.25f, 0.25f, 0.25f, 1.0f });
|
||||
const bool res = image_button(tex_id, size, uv0, uv1);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (!tooltip.empty() && ImGui::IsItemHovered())
|
||||
this->tooltip(tooltip, ImGui::GetFontSize() * 20.0f);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection)
|
||||
{
|
||||
// this is to force the label to the left of the widget:
|
||||
|
@ -1780,6 +1846,17 @@ bool ImGuiWrapper::want_any_input() const
|
|||
return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput;
|
||||
}
|
||||
|
||||
ImFontAtlasCustomRect* ImGuiWrapper::GetTextureCustomRect(const wchar_t& tex_id)
|
||||
{
|
||||
auto item = m_custom_glyph_rects_ids.find(tex_id);
|
||||
return (item != m_custom_glyph_rects_ids.end()) ? ImGui::GetIO().Fonts->GetCustomRectByIndex(m_custom_glyph_rects_ids[tex_id]) : nullptr;
|
||||
}
|
||||
|
||||
void ImGuiWrapper::disable_background_fadeout_animation()
|
||||
{
|
||||
GImGui->DimBgRatio = 1.0f;
|
||||
}
|
||||
|
||||
ImU32 ImGuiWrapper::to_ImU32(const ColorRGBA& color)
|
||||
{
|
||||
return ImGui::GetColorU32({ color.r(), color.g(), color.b(), color.a() });
|
||||
|
@ -2151,12 +2228,18 @@ void ImGuiWrapper::init_font(bool compress)
|
|||
|
||||
int rect_id = io.Fonts->CustomRects.Size; // id of the rectangle added next
|
||||
// add rectangles for the icons to the font atlas
|
||||
for (auto& icon : font_icons)
|
||||
for (auto& icon : font_icons) {
|
||||
m_custom_glyph_rects_ids[icon.first] =
|
||||
io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz, icon_sz, 3.0 * font_scale + icon_sz);
|
||||
for (auto& icon : font_icons_large)
|
||||
}
|
||||
for (auto& icon : font_icons_large) {
|
||||
m_custom_glyph_rects_ids[icon.first] =
|
||||
io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 2, icon_sz * 2, 3.0 * font_scale + icon_sz * 2);
|
||||
for (auto& icon : font_icons_extra_large)
|
||||
}
|
||||
for (auto& icon : font_icons_extra_large) {
|
||||
m_custom_glyph_rects_ids[icon.first] =
|
||||
io.Fonts->AddCustomRectFontGlyph(default_font, icon.first, icon_sz * 4, icon_sz * 4, 3.0 * font_scale + icon_sz * 4);
|
||||
}
|
||||
|
||||
// Build texture atlas
|
||||
unsigned char* pixels;
|
||||
|
@ -2164,55 +2247,37 @@ void ImGuiWrapper::init_font(bool compress)
|
|||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
|
||||
BOOST_LOG_TRIVIAL(trace) << "Build default font texture done. width: " << width << ", height: " << height;
|
||||
|
||||
// Fill rectangles from the SVG-icons
|
||||
for (auto icon : font_icons) {
|
||||
auto load_icon_from_svg = [this, &io, pixels, width, &rect_id](const std::pair<const wchar_t, std::string> icon, int icon_sz) {
|
||||
if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) {
|
||||
assert(rect->Width == icon_sz);
|
||||
assert(rect->Height == icon_sz);
|
||||
unsigned outwidth, outheight;
|
||||
unsigned outwidth, outheight;
|
||||
std::vector<unsigned char> raw_data = load_svg(icon.second, icon_sz, icon_sz, &outwidth, &outheight);
|
||||
const ImU32* pIn = (ImU32*)raw_data.data();
|
||||
for (unsigned y = 0; y < outheight; y++) {
|
||||
ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X);
|
||||
for (unsigned x = 0; x < outwidth; x++)
|
||||
*pOut++ = *pIn++;
|
||||
if (!raw_data.empty()) {
|
||||
const ImU32* pIn = (ImU32*)raw_data.data();
|
||||
for (unsigned y = 0; y < outheight; y++) {
|
||||
ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X);
|
||||
for (unsigned x = 0; x < outwidth; x++)
|
||||
*pOut++ = *pIn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
rect_id++;
|
||||
};
|
||||
|
||||
// Fill rectangles from the SVG-icons
|
||||
for (auto icon : font_icons) {
|
||||
load_icon_from_svg(icon, icon_sz);
|
||||
}
|
||||
|
||||
icon_sz *= 2; // default size of large icon is 32 px
|
||||
for (auto icon : font_icons_large) {
|
||||
if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) {
|
||||
assert(rect->Width == icon_sz);
|
||||
assert(rect->Height == icon_sz);
|
||||
unsigned outwidth, outheight;
|
||||
std::vector<unsigned char> raw_data = load_svg(icon.second, icon_sz, icon_sz, &outwidth, &outheight);
|
||||
const ImU32* pIn = (ImU32*)raw_data.data();
|
||||
for (unsigned y = 0; y < outheight; y++) {
|
||||
ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X);
|
||||
for (unsigned x = 0; x < outwidth; x++)
|
||||
*pOut++ = *pIn++;
|
||||
}
|
||||
}
|
||||
rect_id++;
|
||||
load_icon_from_svg(icon, icon_sz);
|
||||
}
|
||||
|
||||
icon_sz *= 2; // default size of extra large icon is 64 px
|
||||
for (auto icon : font_icons_extra_large) {
|
||||
if (const ImFontAtlas::CustomRect* rect = io.Fonts->GetCustomRectByIndex(rect_id)) {
|
||||
assert(rect->Width == icon_sz);
|
||||
assert(rect->Height == icon_sz);
|
||||
unsigned outwidth, outheight;
|
||||
std::vector<unsigned char> raw_data = load_svg(icon.second, icon_sz, icon_sz, &outwidth, &outheight);
|
||||
const ImU32* pIn = (ImU32*)raw_data.data();
|
||||
for (unsigned y = 0; y < outheight; y++) {
|
||||
ImU32* pOut = (ImU32*)pixels + (rect->Y + y) * width + (rect->X);
|
||||
for (unsigned x = 0; x < outwidth; x++)
|
||||
*pOut++ = *pIn++;
|
||||
}
|
||||
}
|
||||
rect_id++;
|
||||
load_icon_from_svg(icon, icon_sz);
|
||||
}
|
||||
|
||||
// Upload texture to graphics system
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2018 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv, Lukáš Hejl @hejllukas, David Kocík @kocikdav, Vojtěch Král @vojtechkral
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_ImGuiWrapper_hpp_
|
||||
#define slic3r_ImGuiWrapper_hpp_
|
||||
|
||||
|
@ -61,6 +65,7 @@ class ImGuiWrapper
|
|||
#if ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
bool m_requires_extra_frame{ false };
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
std::map<wchar_t, int> m_custom_glyph_rects_ids;
|
||||
std::string m_clipboard_text;
|
||||
|
||||
public:
|
||||
|
@ -117,7 +122,6 @@ public:
|
|||
bool bbl_button(const wxString &label);
|
||||
bool button(const wxString& label, float width, float height);
|
||||
bool radio_button(const wxString &label, bool active);
|
||||
bool image_button();
|
||||
bool input_double(const std::string &label, const double &value, const std::string &format = "%.3f");
|
||||
bool input_double(const wxString &label, const double &value, const std::string &format = "%.3f");
|
||||
bool input_vec3(const std::string &label, const Vec3d &value, float width, const std::string &format = "%.3f");
|
||||
|
@ -150,6 +154,9 @@ public:
|
|||
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
bool image_button(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0.0, 0.0), const ImVec2& uv1 = ImVec2(1.0, 1.0), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0.0, 0.0, 0.0, 0.0), const ImVec4& tint_col = ImVec4(1.0, 1.0, 1.0, 1.0), ImGuiButtonFlags flags = 0);
|
||||
bool image_button(const wchar_t icon, const wxString& tooltip = L"");
|
||||
|
||||
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection); // Use -1 to not mark any option as selected
|
||||
bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel);
|
||||
void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
|
||||
|
@ -184,11 +191,15 @@ public:
|
|||
void reset_requires_extra_frame() { m_requires_extra_frame = false; }
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
|
||||
void disable_background_fadeout_animation();
|
||||
|
||||
static ImU32 to_ImU32(const ColorRGBA& color);
|
||||
static ImVec4 to_ImVec4(const ColorRGBA& color);
|
||||
static ColorRGBA from_ImU32(const ImU32& color);
|
||||
static ColorRGBA from_ImVec4(const ImVec4& color);
|
||||
|
||||
ImFontAtlasCustomRect* GetTextureCustomRect(const wchar_t& tex_id);
|
||||
|
||||
static const ImVec4 COL_GREY_DARK;
|
||||
static const ImVec4 COL_GREY_LIGHT;
|
||||
static const ImVec4 COL_ORANGE_DARK;
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
///|/ Copyright (c) Prusa Research 2022 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "SceneRaycaster.hpp"
|
||||
|
||||
#include "Camera.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Selection.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
@ -35,10 +41,11 @@ std::shared_ptr<SceneRaycasterItem> SceneRaycaster::add_raycaster(EType type, in
|
|||
const Transform3d& trafo, bool use_back_faces)
|
||||
{
|
||||
switch (type) {
|
||||
case EType::Bed: { return m_bed.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
||||
case EType::Bed: { return m_bed.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
||||
case EType::Volume: { return m_volumes.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
||||
case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
||||
default: { assert(false); return nullptr; }
|
||||
case EType::Gizmo: { return m_gizmos.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
||||
case EType::FallbackGizmo: { return m_fallback_gizmos.emplace_back(std::make_shared<SceneRaycasterItem>(encode_id(type, id), raycaster, trafo, use_back_faces)); }
|
||||
default: { assert(false); return nullptr; }
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -60,6 +67,7 @@ void SceneRaycaster::remove_raycasters(EType type)
|
|||
case EType::Bed: { m_bed.clear(); break; }
|
||||
case EType::Volume: { m_volumes.clear(); break; }
|
||||
case EType::Gizmo: { m_gizmos.clear(); break; }
|
||||
case EType::FallbackGizmo: { m_fallback_gizmos.clear(); break; }
|
||||
default: { break; }
|
||||
};
|
||||
}
|
||||
|
@ -84,28 +92,72 @@ void SceneRaycaster::remove_raycaster(std::shared_ptr<SceneRaycasterItem> item)
|
|||
return;
|
||||
}
|
||||
}
|
||||
for (auto it = m_fallback_gizmos.begin(); it != m_fallback_gizmos.end(); ++it) {
|
||||
if (*it == item) {
|
||||
m_fallback_gizmos.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane)
|
||||
SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane) const
|
||||
{
|
||||
// helper class used to return currently selected volume as hit when overlapping with other volumes
|
||||
// to allow the user to click and drag on a selected volume
|
||||
class VolumeKeeper
|
||||
{
|
||||
std::optional<unsigned int> m_selected_volume_id;
|
||||
Vec3f m_closest_hit_pos{ std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max() };
|
||||
bool m_selected_volume_already_found{ false };
|
||||
|
||||
public:
|
||||
VolumeKeeper() {
|
||||
const Selection& selection = wxGetApp().plater()->get_selection();
|
||||
if (selection.is_single_volume() || selection.is_single_modifier()) {
|
||||
const GLVolume* volume = selection.get_first_volume();
|
||||
if (!volume->is_wipe_tower && !volume->is_sla_pad() && !volume->is_sla_support())
|
||||
m_selected_volume_id = *selection.get_volume_idxs().begin();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_active() const { return m_selected_volume_id.has_value(); }
|
||||
const Vec3f& get_closest_hit_pos() const { return m_closest_hit_pos; }
|
||||
bool check_hit_result(const HitResult& hit) {
|
||||
assert(is_active());
|
||||
|
||||
if (m_selected_volume_already_found && hit.type == SceneRaycaster::EType::Volume && hit.position.isApprox(m_closest_hit_pos))
|
||||
return false;
|
||||
|
||||
if (hit.type == SceneRaycaster::EType::Volume)
|
||||
m_selected_volume_already_found = *m_selected_volume_id == (unsigned int)decode_id(hit.type, hit.raycaster_id);
|
||||
|
||||
m_closest_hit_pos = hit.position;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
VolumeKeeper volume_keeper;
|
||||
|
||||
double closest_hit_squared_distance = std::numeric_limits<double>::max();
|
||||
auto is_closest = [&closest_hit_squared_distance](const Camera& camera, const Vec3f& hit) {
|
||||
auto is_closest = [&closest_hit_squared_distance, &volume_keeper](const Camera& camera, const Vec3f& hit) {
|
||||
const double hit_squared_distance = (camera.get_position() - hit.cast<double>()).squaredNorm();
|
||||
const bool ret = hit_squared_distance < closest_hit_squared_distance;
|
||||
bool ret = hit_squared_distance < closest_hit_squared_distance;
|
||||
if (volume_keeper.is_active())
|
||||
ret |= hit.isApprox(volume_keeper.get_closest_hit_pos());
|
||||
if (ret)
|
||||
closest_hit_squared_distance = hit_squared_distance;
|
||||
return ret;
|
||||
};
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
m_last_hit.reset();
|
||||
const_cast<std::optional<HitResult>*>(&m_last_hit)->reset();
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
HitResult ret;
|
||||
|
||||
auto test_raycasters = [this, is_closest, clipping_plane](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) {
|
||||
auto test_raycasters = [this, is_closest, clipping_plane, &volume_keeper](EType type, const Vec2d& mouse_pos, const Camera& camera, HitResult& ret) {
|
||||
const ClippingPlane* clip_plane = (clipping_plane != nullptr && type == EType::Volume) ? clipping_plane : nullptr;
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>>* raycasters = get_raycasters(type);
|
||||
const std::vector<std::shared_ptr<SceneRaycasterItem>>* raycasters = get_raycasters(type);
|
||||
const Vec3f camera_forward = camera.get_dir_forward().cast<float>();
|
||||
HitResult current_hit = { type };
|
||||
for (std::shared_ptr<SceneRaycasterItem> item : *raycasters) {
|
||||
|
@ -117,9 +169,14 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
|||
if (item->get_raycaster()->closest_hit(mouse_pos, trafo, camera, current_hit.position, current_hit.normal, clip_plane)) {
|
||||
current_hit.position = (trafo * current_hit.position.cast<double>()).cast<float>();
|
||||
current_hit.normal = (trafo.matrix().block(0, 0, 3, 3).inverse().transpose() * current_hit.normal.cast<double>()).normalized().cast<float>();
|
||||
if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f){
|
||||
if (item->use_back_faces() || current_hit.normal.dot(camera_forward) < 0.0f) {
|
||||
if (is_closest(camera, current_hit.position)) {
|
||||
ret = current_hit;
|
||||
if (volume_keeper.is_active()) {
|
||||
if (volume_keeper.check_hit_result(current_hit))
|
||||
ret = current_hit;
|
||||
}
|
||||
else
|
||||
ret = current_hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +186,9 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
|||
if (!m_gizmos.empty())
|
||||
test_raycasters(EType::Gizmo, mouse_pos, camera, ret);
|
||||
|
||||
if (!m_fallback_gizmos.empty() && !ret.is_valid())
|
||||
test_raycasters(EType::FallbackGizmo, mouse_pos, camera, ret);
|
||||
|
||||
if (!m_gizmos_on_top || !ret.is_valid()) {
|
||||
if (camera.is_looking_downward() && !m_bed.empty())
|
||||
test_raycasters(EType::Bed, mouse_pos, camera, ret);
|
||||
|
@ -140,7 +200,7 @@ SceneRaycaster::HitResult SceneRaycaster::hit(const Vec2d& mouse_pos, const Came
|
|||
ret.raycaster_id = decode_id(ret.type, ret.raycaster_id);
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
m_last_hit = ret;
|
||||
*const_cast<std::optional<HitResult>*>(&m_last_hit) = ret;
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
return ret;
|
||||
}
|
||||
|
@ -171,6 +231,39 @@ void SceneRaycaster::render_hit(const Camera& camera)
|
|||
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
size_t SceneRaycaster::active_beds_count() const {
|
||||
size_t count = 0;
|
||||
for (const auto& b : m_bed) {
|
||||
if (b->is_active())
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
size_t SceneRaycaster::active_volumes_count() const {
|
||||
size_t count = 0;
|
||||
for (const auto& v : m_volumes) {
|
||||
if (v->is_active())
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
size_t SceneRaycaster::active_gizmos_count() const {
|
||||
size_t count = 0;
|
||||
for (const auto& g : m_gizmos) {
|
||||
if (g->is_active())
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
size_t SceneRaycaster::active_fallback_gizmos_count() const {
|
||||
size_t count = 0;
|
||||
for (const auto& g : m_fallback_gizmos) {
|
||||
if (g->is_active())
|
||||
++count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters(EType type)
|
||||
|
@ -181,6 +274,22 @@ std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters
|
|||
case EType::Bed: { ret = &m_bed; break; }
|
||||
case EType::Volume: { ret = &m_volumes; break; }
|
||||
case EType::Gizmo: { ret = &m_gizmos; break; }
|
||||
case EType::FallbackGizmo: { ret = &m_fallback_gizmos; break; }
|
||||
default: { break; }
|
||||
}
|
||||
assert(ret != nullptr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<SceneRaycasterItem>>* SceneRaycaster::get_raycasters(EType type) const
|
||||
{
|
||||
const std::vector<std::shared_ptr<SceneRaycasterItem>>* ret = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
case EType::Bed: { ret = &m_bed; break; }
|
||||
case EType::Volume: { ret = &m_volumes; break; }
|
||||
case EType::Gizmo: { ret = &m_gizmos; break; }
|
||||
case EType::FallbackGizmo: { ret = &m_fallback_gizmos; break; }
|
||||
default: { break; }
|
||||
}
|
||||
assert(ret != nullptr);
|
||||
|
@ -194,6 +303,7 @@ int SceneRaycaster::base_id(EType type)
|
|||
case EType::Bed: { return int(EIdBase::Bed); }
|
||||
case EType::Volume: { return int(EIdBase::Volume); }
|
||||
case EType::Gizmo: { return int(EIdBase::Gizmo); }
|
||||
case EType::FallbackGizmo: { return int(EIdBase::FallbackGizmo); }
|
||||
default: { break; }
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2022 - 2023 Oleksandra Iushchenko @YuSanka, Enrico Turri @enricoturri1966, Tomáš Mészáros @tamasmeszaros
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_SceneRaycaster_hpp_
|
||||
#define slic3r_SceneRaycaster_hpp_
|
||||
|
||||
|
@ -42,14 +46,16 @@ public:
|
|||
None,
|
||||
Bed,
|
||||
Volume,
|
||||
Gizmo
|
||||
Gizmo,
|
||||
FallbackGizmo // Is used for gizmo grabbers which will be hit after all grabbers of Gizmo type
|
||||
};
|
||||
|
||||
enum class EIdBase
|
||||
{
|
||||
Bed = 0,
|
||||
Volume = 1000,
|
||||
Gizmo = 1000000
|
||||
Gizmo = 1000000,
|
||||
FallbackGizmo = 2000000
|
||||
};
|
||||
|
||||
struct HitResult
|
||||
|
@ -66,6 +72,7 @@ private:
|
|||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_bed;
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_volumes;
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_gizmos;
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>> m_fallback_gizmos;
|
||||
|
||||
// When set to true, if checking gizmos returns a valid hit,
|
||||
// the search is not performed on other types
|
||||
|
@ -87,10 +94,11 @@ public:
|
|||
void remove_raycaster(std::shared_ptr<SceneRaycasterItem> item);
|
||||
|
||||
std::vector<std::shared_ptr<SceneRaycasterItem>>* get_raycasters(EType type);
|
||||
const std::vector<std::shared_ptr<SceneRaycasterItem>>* get_raycasters(EType type) const;
|
||||
|
||||
void set_gizmos_on_top(bool value) { m_gizmos_on_top = value; }
|
||||
|
||||
HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr);
|
||||
HitResult hit(const Vec2d& mouse_pos, const Camera& camera, const ClippingPlane* clipping_plane = nullptr) const;
|
||||
|
||||
#if ENABLE_RAYCAST_PICKING_DEBUG
|
||||
void render_hit(const Camera& camera);
|
||||
|
@ -98,11 +106,17 @@ public:
|
|||
size_t beds_count() const { return m_bed.size(); }
|
||||
size_t volumes_count() const { return m_volumes.size(); }
|
||||
size_t gizmos_count() const { return m_gizmos.size(); }
|
||||
size_t fallback_gizmos_count() const { return m_fallback_gizmos.size(); }
|
||||
size_t active_beds_count() const;
|
||||
size_t active_volumes_count() const;
|
||||
size_t active_gizmos_count() const;
|
||||
size_t active_fallback_gizmos_count() const;
|
||||
#endif // ENABLE_RAYCAST_PICKING_DEBUG
|
||||
|
||||
static int decode_id(EType type, int id);
|
||||
|
||||
private:
|
||||
static int encode_id(EType type, int id);
|
||||
static int decode_id(EType type, int id);
|
||||
static int base_id(EType type);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
///|/ Copyright (c) Prusa Research 2019 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Lukáš Matěna @lukasmatena, Filip Sykala @Jony01
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "Selection.hpp"
|
||||
|
||||
|
@ -710,6 +714,17 @@ bool Selection::contains_any_volume(const std::vector<unsigned int>& volume_idxs
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Selection::contains_sinking_volumes(bool ignore_modifiers) const
|
||||
{
|
||||
for (const GLVolume* v : *m_volumes) {
|
||||
if (!ignore_modifiers || !v->is_modifier) {
|
||||
if (v->is_sinking())
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Selection::matches(const std::vector<unsigned int>& volume_idxs) const
|
||||
{
|
||||
unsigned int count = 0;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
///|/ Copyright (c) Prusa Research 2019 - 2023 Enrico Turri @enricoturri1966, Oleksandra Iushchenko @YuSanka, Filip Sykala @Jony01, Lukáš Matěna @lukasmatena, Vojtěch Bubník @bubnikv
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_GUI_Selection_hpp_
|
||||
#define slic3r_GUI_Selection_hpp_
|
||||
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "GUI_Geometry.hpp"
|
||||
#include "GLModel.hpp"
|
||||
|
||||
#include <set>
|
||||
|
@ -26,58 +31,6 @@ using ModelObjectPtrs = std::vector<ModelObject*>;
|
|||
|
||||
|
||||
namespace GUI {
|
||||
class TransformationType
|
||||
{
|
||||
public:
|
||||
enum Enum {
|
||||
// Transforming in a world coordinate system
|
||||
World = 0,
|
||||
// Transforming in a local coordinate system
|
||||
Local = 1,
|
||||
// Absolute transformations, allowed in local coordinate system only.
|
||||
Absolute = 0,
|
||||
// Relative transformations, allowed in both local and world coordinate system.
|
||||
Relative = 2,
|
||||
// For group selection, the transformation is performed as if the group made a single solid body.
|
||||
Joint = 0,
|
||||
// For group selection, the transformation is performed on each object independently.
|
||||
Independent = 4,
|
||||
|
||||
World_Relative_Joint = World | Relative | Joint,
|
||||
World_Relative_Independent = World | Relative | Independent,
|
||||
Local_Absolute_Joint = Local | Absolute | Joint,
|
||||
Local_Absolute_Independent = Local | Absolute | Independent,
|
||||
Local_Relative_Joint = Local | Relative | Joint,
|
||||
Local_Relative_Independent = Local | Relative | Independent,
|
||||
};
|
||||
|
||||
TransformationType() : m_value(World) {}
|
||||
TransformationType(Enum value) : m_value(value) {}
|
||||
TransformationType& operator=(Enum value) { m_value = value; return *this; }
|
||||
|
||||
Enum operator()() const { return m_value; }
|
||||
bool has(Enum v) const { return ((unsigned int)m_value & (unsigned int)v) != 0; }
|
||||
|
||||
void set_world() { this->remove(Local); }
|
||||
void set_local() { this->add(Local); }
|
||||
void set_absolute() { this->remove(Relative); }
|
||||
void set_relative() { this->add(Relative); }
|
||||
void set_joint() { this->remove(Independent); }
|
||||
void set_independent() { this->add(Independent); }
|
||||
|
||||
bool world() const { return !this->has(Local); }
|
||||
bool local() const { return this->has(Local); }
|
||||
bool absolute() const { return !this->has(Relative); }
|
||||
bool relative() const { return this->has(Relative); }
|
||||
bool joint() const { return !this->has(Independent); }
|
||||
bool independent() const { return this->has(Independent); }
|
||||
|
||||
private:
|
||||
void add(Enum v) { m_value = Enum((unsigned int)m_value | (unsigned int)v); }
|
||||
void remove(Enum v) { m_value = Enum((unsigned int)m_value & (~(unsigned int)v)); }
|
||||
|
||||
Enum m_value;
|
||||
};
|
||||
|
||||
class Selection
|
||||
{
|
||||
|
@ -307,6 +260,8 @@ public:
|
|||
bool contains_all_volumes(const std::vector<unsigned int>& volume_idxs) const;
|
||||
// returns true if the selection contains at least one of the given indices
|
||||
bool contains_any_volume(const std::vector<unsigned int>& volume_idxs) const;
|
||||
// returns true if the selection contains any sinking volume
|
||||
bool contains_sinking_volumes(bool ignore_modifiers = true) const;
|
||||
// returns true if the selection contains all and only the given indices
|
||||
bool matches(const std::vector<unsigned int>& volume_idxs) const;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue