mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-12 18:31:18 +00:00
Switch to the Fast view while UV islands are selected in the texture gizmo
The island highlight was hidden under the Normal view's relief. Normal comes back once no island is selected, unless another view was picked in the meantime.
This commit is contained in:
@@ -562,6 +562,11 @@ void GLGizmoTextureDisplacement::on_shutdown()
|
||||
m_shaded_island_delta = Eigen::Matrix<float, 2, 3>::Identity();
|
||||
m_island_drag_active = false;
|
||||
m_island_move_set.clear();
|
||||
// The pane closes below, taking the highlight with it, and the view must not open the next session in a
|
||||
// Fast the user never picked. Set directly: nothing is to be queued against a gizmo that is closing.
|
||||
if (m_island_view.switched)
|
||||
m_use_shaded_preview = false;
|
||||
m_island_view = IslandViewFollow{};
|
||||
m_adjust_texture_mode = false;
|
||||
m_seam_edit_mode = false;
|
||||
m_adjust_drag_handle = AdjustHandle::None;
|
||||
@@ -727,18 +732,13 @@ void GLGizmoTextureDisplacement::render_painter_gizmo()
|
||||
|
||||
// The UV editor's island selection, shown on the model. Polled here rather than pushed: the pane
|
||||
// changes its selection in its own mouse handling, and a compare of a few ints per frame is free.
|
||||
{
|
||||
const TextureDisplacementLayer *al = active_layer();
|
||||
const UVEditorCanvas *uv_canvas = wxGetApp().plater()->get_uv_editor_canvas();
|
||||
if (m_show_uv_editor && al != nullptr && al->projection_method == TextureProjectionMethod::LSCM &&
|
||||
uv_canvas != nullptr && !m_uv_editor_unwrap.empty()) {
|
||||
if (uv_canvas->selected_islands() != m_island_overlay_selection)
|
||||
rebuild_island_overlay(uv_canvas->selected_islands());
|
||||
render_island_overlay();
|
||||
} else if (m_island_overlay_glmodel.is_initialized()) {
|
||||
m_island_overlay_glmodel.reset();
|
||||
m_island_overlay_selection.clear();
|
||||
}
|
||||
if (const std::vector<int> *islands = island_highlight_selection()) {
|
||||
if (*islands != m_island_overlay_selection)
|
||||
rebuild_island_overlay(*islands);
|
||||
render_island_overlay();
|
||||
} else if (m_island_overlay_glmodel.is_initialized()) {
|
||||
m_island_overlay_glmodel.reset();
|
||||
m_island_overlay_selection.clear();
|
||||
}
|
||||
|
||||
// Diagnostic overlays, drawn on top of whatever preview is active (both pull toward the camera
|
||||
@@ -1926,6 +1926,16 @@ void GLGizmoTextureDisplacement::render_paint_overlay(GLModel &overlay)
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
const std::vector<int> *GLGizmoTextureDisplacement::island_highlight_selection()
|
||||
{
|
||||
const TextureDisplacementLayer *al = active_layer();
|
||||
const UVEditorCanvas *uv_canvas = wxGetApp().plater()->get_uv_editor_canvas();
|
||||
if (m_show_uv_editor && al != nullptr && al->projection_method == TextureProjectionMethod::LSCM &&
|
||||
uv_canvas != nullptr && !m_uv_editor_unwrap.empty())
|
||||
return &uv_canvas->selected_islands();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GLGizmoTextureDisplacement::rebuild_island_overlay(const std::vector<int> &selection)
|
||||
{
|
||||
m_island_overlay_glmodel.reset();
|
||||
@@ -2651,6 +2661,37 @@ void GLGizmoTextureDisplacement::apply_view_mode(int mode)
|
||||
m_parent.set_as_dirty();
|
||||
}
|
||||
|
||||
int GLGizmoTextureDisplacement::view_mode() const
|
||||
{
|
||||
return m_use_shaded_preview ? 1 :
|
||||
m_uv_check_mode == UVCheckMode::Checker ? 2 :
|
||||
m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0;
|
||||
}
|
||||
|
||||
int GLGizmoTextureDisplacement::IslandViewFollow::step(bool shown_now, int view, bool debug_open)
|
||||
{
|
||||
shown = shown_now;
|
||||
if (!shown) {
|
||||
// The view is back to Normal already if a debug stage took it, and Normal applied over a stage would
|
||||
// queue a preview job that replaces the stage being inspected.
|
||||
const bool restore = switched && view == 1;
|
||||
switched = keep_normal = false;
|
||||
return restore ? 0 : -1;
|
||||
}
|
||||
if (view != 0 || keep_normal || debug_open)
|
||||
return -1;
|
||||
switched = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void GLGizmoTextureDisplacement::follow_island_highlight()
|
||||
{
|
||||
const std::vector<int> *islands = island_highlight_selection();
|
||||
const int view = m_island_view.step(islands != nullptr && !islands->empty(), view_mode(), m_debug_stage >= 0);
|
||||
if (view >= 0)
|
||||
apply_view_mode(view);
|
||||
}
|
||||
|
||||
void GLGizmoTextureDisplacement::push_uv_pane_state()
|
||||
{
|
||||
UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas();
|
||||
@@ -2891,7 +2932,8 @@ void GLGizmoTextureDisplacement::run_uv_command(int cmd, float value)
|
||||
rebuild_preview();
|
||||
}
|
||||
}
|
||||
// FrameAll/ToggleSnap are handled inside the canvas; ProjectFromView is not wired yet.
|
||||
// FrameAll/ToggleSnap are handled inside the canvas; ProjectFromView is not wired yet. SelectionChanged
|
||||
// only asks for this frame: follow_island_highlight() and the overlay read the selection back themselves.
|
||||
m_parent.set_as_dirty();
|
||||
}
|
||||
|
||||
@@ -5835,6 +5877,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
GizmoImguiBegin(get_name(), flags);
|
||||
ensure_panel_icons();
|
||||
process_uv_commands(); // clicks from the UV editor pane, run here where the GL context is current
|
||||
// After the pane's commands, which can close it or change the view, and before the View row, so the row
|
||||
// lights the view this frame ends up in and a click on it has the last word.
|
||||
follow_island_highlight();
|
||||
const float previous_body_h = m_panel_body_h;
|
||||
const float previous_footer_h = m_panel_footer_h;
|
||||
|
||||
@@ -6323,10 +6368,9 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
// ---- View: Normal / Fast / Checker / Distortion as one group, Wireframe on its own ----
|
||||
// The underlying state stays m_use_shaded_preview + m_uv_check_mode.
|
||||
{
|
||||
const int cur_mode = m_use_shaded_preview ? 1 :
|
||||
m_uv_check_mode == UVCheckMode::Checker ? 2 :
|
||||
m_uv_check_mode == UVCheckMode::Distortion ? 3 : 0;
|
||||
const int cur_mode = view_mode();
|
||||
int new_mode = cur_mode;
|
||||
int picked = -1; // the button clicked, the active one included
|
||||
bool wf_toggle = false;
|
||||
bool open_uv_editor = false;
|
||||
// Checker and Distortion both draw *the unwrap* - the first the texture grid laid over it, the second
|
||||
@@ -6349,19 +6393,19 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + m_imgui->scaled(2.6f)));
|
||||
if (icon_toggle(701, "texture_displacement_real_preview.svg", cur_mode == 0, icon_md, _L("Normal"),
|
||||
_L("Normal - the real geometry, exactly what Bake will produce. Slower to update")))
|
||||
new_mode = 0;
|
||||
new_mode = picked = 0;
|
||||
ImGui::SameLine(0.f, gap_s);
|
||||
if (icon_toggle(702, "texture_displacement_fast_preview.svg", cur_mode == 1, icon_md, _L("Fast"),
|
||||
_L("Fast - the relief is only shaded on, not built, and only for the layer you are "
|
||||
"editing. Updates instantly while you paint")))
|
||||
new_mode = 1;
|
||||
new_mode = picked = 1;
|
||||
ImGui::SameLine(0.f, gap_s);
|
||||
if (icon_toggle(703, "texture_displacement_checker.svg", cur_mode == 2, icon_md, _L("Checker"),
|
||||
_L("Checker - a test grid instead of the texture. Where the squares stay square the "
|
||||
"texture is undistorted; where they stretch, it will too. Opens the UV editor if "
|
||||
"it is closed"),
|
||||
uv_view_na)) {
|
||||
new_mode = 2;
|
||||
new_mode = picked = 2;
|
||||
open_uv_editor = true;
|
||||
}
|
||||
ImGui::SameLine(0.f, gap_s);
|
||||
@@ -6369,7 +6413,7 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
_L("Distortion - blue-to-red stretch heatmap over the unwrap. Opens the UV editor if "
|
||||
"it is closed"),
|
||||
uv_view_na)) {
|
||||
new_mode = 3;
|
||||
new_mode = picked = 3;
|
||||
open_uv_editor = true;
|
||||
}
|
||||
vsep(icon_md);
|
||||
@@ -6393,6 +6437,8 @@ void GLGizmoTextureDisplacement::on_render_input_window(float x, float y, float
|
||||
if (new_mode == cur_mode)
|
||||
update_uv_editor(); // otherwise apply_view_mode() below does it
|
||||
}
|
||||
if (picked >= 0)
|
||||
m_island_view.picked(picked);
|
||||
if (new_mode != cur_mode)
|
||||
apply_view_mode(new_mode);
|
||||
if (wf_toggle) {
|
||||
|
||||
@@ -197,6 +197,27 @@ public:
|
||||
};
|
||||
static PaintedColors painted_colors(const TriangleMesh &mesh, const TriangleSelector::TriangleSplittingData &paint);
|
||||
|
||||
// The view while the UV editor's selected islands are highlighted on the model. The highlight lies on the
|
||||
// undisplaced surface, so the Normal view's relief hides it wherever it rises: Normal gives way to Fast
|
||||
// while islands are selected and comes back once none are. A view the user clicks in the View row is
|
||||
// theirs - the pane's background buttons are not a pick, Height only meaning "the texture view".
|
||||
struct IslandViewFollow
|
||||
{
|
||||
bool shown = false; // the highlight, as of the last step()
|
||||
bool switched = false; // Fast was switched on for the highlight, so Normal comes back once it is gone
|
||||
bool keep_normal = false; // the user picked Normal in the View row while the highlight was shown
|
||||
// The view to apply this frame (0 Normal, 1 Fast), or -1 for none. `view` is the one on screen, numbered as
|
||||
// apply_view_mode() takes it; `debug_open` a debug stage on screen, which holds Normal itself.
|
||||
int step(bool shown_now, int view, bool debug_open);
|
||||
// A View row button clicked, the active one included. The row is drawn after step() in the same frame,
|
||||
// so `shown` is this frame's.
|
||||
void picked(int view)
|
||||
{
|
||||
switched = false;
|
||||
keep_normal = shown && view == 0;
|
||||
}
|
||||
};
|
||||
|
||||
void render_painter_gizmo() override;
|
||||
|
||||
// Intercepts mouse input while "Adjust Texture" mode is on (dragging the on-canvas offset/
|
||||
@@ -513,6 +534,11 @@ private:
|
||||
// The panel's view modes: 0 Normal, 1 Fast, 2 Checker, 3 Distortion. Shared by the View row and the pane's
|
||||
// background buttons, so both switch views the same way.
|
||||
void apply_view_mode(int mode);
|
||||
// The view on screen, numbered as apply_view_mode() takes it.
|
||||
int view_mode() const;
|
||||
// Run once a frame, after the pane's commands: switches the view as m_island_view says.
|
||||
void follow_island_highlight();
|
||||
IslandViewFollow m_island_view;
|
||||
// The pane header's thumbnail of the active layer's texture, rebuilt only when its image changes.
|
||||
static constexpr int UV_THUMB_PX = 64;
|
||||
const void *m_uv_thumb_source = nullptr;
|
||||
@@ -744,6 +770,8 @@ private:
|
||||
// in place. Rebuilt whenever the pane's selection differs from the one it was built for.
|
||||
GLModel m_island_overlay_glmodel;
|
||||
std::vector<int> m_island_overlay_selection;
|
||||
// The pane's selection while the overlay applies - the pane open over an unwrapped LSCM layer - else null.
|
||||
const std::vector<int> *island_highlight_selection();
|
||||
void rebuild_island_overlay(const std::vector<int> &selection);
|
||||
void render_island_overlay();
|
||||
// Set on every paint event, cleared when the overlay is rebuilt in render_painter_gizmo(). Kept
|
||||
|
||||
@@ -939,7 +939,8 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt)
|
||||
m_active_edge = (hit.first >= 0 && is_edge_selected(hit)) ? hit : std::pair<int, int>{ -1, -1 };
|
||||
m_gesture = (m_active_edge.first >= 0) ? Gesture::MoveEdge : Gesture::Pan;
|
||||
} else {
|
||||
const int hit = island_at(uv);
|
||||
const std::vector<int> selection_before = m_selection;
|
||||
const int hit = island_at(uv);
|
||||
if (hit >= 0) {
|
||||
if (evt.ShiftDown()) {
|
||||
// Shift adds to the selection (and makes the clicked one the new primary).
|
||||
@@ -969,6 +970,10 @@ void UVEditorCanvas::on_mouse(wxMouseEvent &evt)
|
||||
m_selected_island = -1;
|
||||
}
|
||||
m_gesture = (m_selected_island >= 0) ? Gesture::MoveIsland : Gesture::Pan;
|
||||
// The owner shows the selection on the model and would not hear of this click otherwise in time: a
|
||||
// click that clears the selection starts no gesture, and one that sets it reports only on release.
|
||||
if (m_selection != selection_before)
|
||||
run_command(Command::SelectionChanged);
|
||||
}
|
||||
grab_mouse();
|
||||
Refresh();
|
||||
|
||||
@@ -108,10 +108,12 @@ public:
|
||||
// the command callback - the canvas has the selection and the view, the gizmo has the layer.
|
||||
// `value` carries the new setting for the Set* commands (an angle, a flag as 0/1, or an index into
|
||||
// SelectMode / Background) and is 0 otherwise.
|
||||
// SelectionChanged is sent by the canvas itself when a click changes the island selection. It carries
|
||||
// nothing: the owner reads selected_islands() back, and only needs telling that it should look.
|
||||
enum class Command {
|
||||
FrameAll, ToggleSnap, AverageScale, CutSelectedIsland, ProjectFromView, JoinSelected, UnjoinSelected,
|
||||
Unwrap, SetSeamAngle, SetConnectIslands, SetSelectMode, SetMarkSeams, SetSeamPath, ClearSeams, ClearUVEdits,
|
||||
SetBackground, PickTexture, PaneClosed
|
||||
SetBackground, PickTexture, PaneClosed, SelectionChanged
|
||||
};
|
||||
void run_command(Command cmd, float value = 0.f);
|
||||
using CommandFn = std::function<void(Command, float)>;
|
||||
|
||||
@@ -37,6 +37,7 @@ add_executable(${_TEST_NAME}_tests
|
||||
test_plugin_json_depth.cpp
|
||||
test_shortcuts.cpp
|
||||
test_texture_color_palette.cpp
|
||||
test_texture_island_view.cpp
|
||||
test_file_url.cpp
|
||||
test_user_manager.cpp
|
||||
../fff_print/test_helpers.cpp
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// How the texture displacement gizmo's view follows the UV editor's island highlight. The rules live in
|
||||
// libslic3r_gui; this is the suite that links it. Same Windows include prologue as
|
||||
// test_texture_color_palette.cpp (wx pulls in <windows.h>; keep WIN32_LEAN_AND_MEAN / NOMINMAX ahead of it).
|
||||
#ifdef WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp"
|
||||
|
||||
using Follow = Slic3r::GUI::GLGizmoTextureDisplacement::IslandViewFollow;
|
||||
|
||||
namespace {
|
||||
|
||||
// The View row's indices, and step()'s "apply nothing".
|
||||
constexpr int NORMAL = 0, FAST = 1, CHECKER = 2, DISTORTION = 3, NONE = -1;
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("a Normal view gives way to Fast while islands are selected and comes back once none are", "[TextureIslandView][TextureDisplacement]")
|
||||
{
|
||||
Follow f;
|
||||
CHECK(f.step(false, NORMAL, false) == NONE);
|
||||
CHECK(f.step(true, NORMAL, false) == FAST);
|
||||
CHECK(f.step(true, FAST, false) == NONE);
|
||||
CHECK(f.step(false, FAST, false) == NORMAL);
|
||||
CHECK(f.step(false, NORMAL, false) == NONE);
|
||||
}
|
||||
|
||||
TEST_CASE("a Fast, Checker or Distortion view is left alone by the island highlight", "[TextureIslandView][TextureDisplacement]")
|
||||
{
|
||||
const int view = GENERATE(FAST, CHECKER, DISTORTION);
|
||||
Follow f;
|
||||
CHECK(f.step(true, view, false) == NONE);
|
||||
CHECK(f.step(false, view, false) == NONE);
|
||||
}
|
||||
|
||||
TEST_CASE("arriving in Normal with islands already selected switches to Fast", "[TextureIslandView][TextureDisplacement]")
|
||||
{
|
||||
// The pane's Height button over Checker lands in Normal with the islands still selected.
|
||||
Follow f;
|
||||
CHECK(f.step(true, CHECKER, false) == NONE);
|
||||
CHECK(f.step(true, NORMAL, false) == FAST);
|
||||
CHECK(f.step(false, FAST, false) == NORMAL);
|
||||
}
|
||||
|
||||
TEST_CASE("Normal picked while islands are selected stays until they are deselected", "[TextureIslandView][TextureDisplacement]")
|
||||
{
|
||||
Follow f;
|
||||
REQUIRE(f.step(true, NORMAL, false) == FAST);
|
||||
f.picked(NORMAL);
|
||||
CHECK(f.step(true, NORMAL, false) == NONE);
|
||||
// A debug stage opening and closing over it changes nothing.
|
||||
CHECK(f.step(true, NORMAL, true) == NONE);
|
||||
CHECK(f.step(true, NORMAL, false) == NONE);
|
||||
CHECK(f.step(false, NORMAL, false) == NONE);
|
||||
CHECK(f.step(true, NORMAL, false) == FAST);
|
||||
}
|
||||
|
||||
TEST_CASE("Fast picked while it was switched on stays after deselecting", "[TextureIslandView][TextureDisplacement]")
|
||||
{
|
||||
Follow f;
|
||||
REQUIRE(f.step(true, NORMAL, false) == FAST);
|
||||
f.picked(FAST);
|
||||
CHECK(f.step(false, FAST, false) == NONE);
|
||||
}
|
||||
|
||||
TEST_CASE("Normal picked with nothing selected does not hold off the next switch", "[TextureIslandView][TextureDisplacement]")
|
||||
{
|
||||
Follow f;
|
||||
CHECK(f.step(false, NORMAL, false) == NONE);
|
||||
f.picked(NORMAL);
|
||||
CHECK(f.step(true, NORMAL, false) == FAST);
|
||||
}
|
||||
|
||||
TEST_CASE("a debug stage holds Normal while it is open, and the switch follows once it closes", "[TextureIslandView][TextureDisplacement]")
|
||||
{
|
||||
Follow f;
|
||||
REQUIRE(f.step(true, NORMAL, false) == FAST);
|
||||
// The stage takes Normal itself, with the islands still selected.
|
||||
CHECK(f.step(true, NORMAL, true) == NONE);
|
||||
CHECK(f.step(true, NORMAL, false) == FAST);
|
||||
}
|
||||
|
||||
TEST_CASE("islands deselected while a debug stage is open apply no view over it", "[TextureIslandView][TextureDisplacement]")
|
||||
{
|
||||
Follow f;
|
||||
REQUIRE(f.step(true, NORMAL, false) == FAST);
|
||||
CHECK(f.step(false, NORMAL, true) == NONE);
|
||||
CHECK(f.step(false, NORMAL, false) == NONE);
|
||||
}
|
||||
Reference in New Issue
Block a user