OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::GUI::UVEditorCanvas Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/slic3r/GUI/UVEditorCanvas.hpp>

Inheritance diagram for Slic3r::GUI::UVEditorCanvas:
Collaboration diagram for Slic3r::GUI::UVEditorCanvas:

Classes

struct  Islands
struct  PaneState

Public Types

enum class  Command {
  FrameAll , ToggleSnap , AverageScale , CutSelectedIsland ,
  ProjectFromView , JoinSelected , UnjoinSelected , Unwrap ,
  SetSeamAngle , SetConnectIslands , SetSelectMode , SetMarkSeams ,
  SetSeamPath , ClearSeams , ClearUVEdits , SetBackground ,
  PickTexture , PaneClosed , SelectionChanged
}
enum class  Background { Height , Checker , Distortion }
enum class  SelectMode { Island , Vertex , Edge }
using IslandTransform = Eigen::Matrix<float, 2, 3>
using CommandFn = std::function<void(Command, float)>
using PaneStateFn = std::function<void(const PaneState &)>
using StatusFn = std::function<void(const wxString &)>
using IslandEditFn
using UVVertexEditFn = std::function<void(const std::vector<std::pair<int, Vec2f>> &edits)>

Public Member Functions

 UVEditorCanvas (wxWindow *parent)
void set_islands (Islands islands)
void set_island_transforms (std::vector< IslandTransform > transforms)
void set_island_fill_colors (std::vector< ColorRGBA > colors)
void set_uv_transform (float tiling_scale, float rotation_deg, bool tile_enabled, bool tile_mirrored)
void set_background_texture (const std::vector< unsigned char > &grayscale_pixels, int width, int height)
void set_snap_enabled (bool enabled)
bool snap_enabled () const
void run_command (Command cmd, float value=0.f)
void set_command_callback (CommandFn fn)
void set_pane_state (PaneState state)
const PaneState & pane_state () const
void set_pane_state_callback (PaneStateFn fn)
void set_status_callback (StatusFn fn)
void set_island_edit_callback (IslandEditFn fn)
void set_select_mode (SelectMode mode)
SelectMode select_mode () const
void set_vertex_edit_callback (UVVertexEditFn fn)
int selected_island () const
bool has_islands () const
const std::vector< int > & selected_islands () const
void reset_view ()
void invalidate_gl ()

Private Types

enum class  Gesture {
  None , Pan , MoveIsland , RotateIsland ,
  RotateIslandModal , ScaleIslandModal , MoveVertex , MoveEdge
}

Private Member Functions

void on_paint (wxPaintEvent &evt)
void on_size (wxSizeEvent &evt)
void on_mouse (wxMouseEvent &evt)
void on_capture_lost (wxMouseCaptureLostEvent &evt)
void on_key (wxKeyEvent &evt)
void on_leave (wxMouseEvent &evt)
void on_erase_background (wxEraseEvent &evt)
void render ()
void rebuild_island_models ()
void rebuild_background_texture ()
void rebuild_background_quad ()
void rebuild_grid ()
void content_bounds (Vec2f &min_uv, Vec2f &max_uv) const
void framed_bounds (Vec2f &min_uv, Vec2f &max_uv) const
void fit_view_to_content ()
void view_half_extents (float &half_w, float &half_h) const
Vec2f screen_to_uv (const wxPoint &px) const
Vec2f island_uv (size_t vertex) const
int island_at (const Vec2f &uv) const
int vertex_at (const Vec2f &uv) const
std::pair< int, int > edge_at (const Vec2f &uv) const
void move_vertex_raw (int unwrapped_vertex, const Vec2f &delta_uv)
Vec2f island_centroid (int island) const
Vec2f uv_delta_to_unwrap (const Vec2f &delta_uv) const
Vec2f snap_correction (int island) const
void end_gesture ()
void cancel_gesture ()
void grab_mouse ()
void drop_mouse ()
void update_status ()
void update_hover (const wxPoint &pos)
bool is_vertex_selected (int v) const
bool is_edge_selected (const std::pair< int, int > &e) const
std::vector< int > selected_edge_endpoints () const
bool is_selected (int island) const
void rebuild_rotation_dial ()
float island_rotation_deg (int island) const

Private Attributes

wxGLContext * m_context = nullptr
Islands m_islands
std::vector< IslandTransform > m_transforms
std::vector< ColorRGBA > m_island_fill_colors
std::vector< std::vector< int > > m_island_boundary_verts
std::vector< std::vector< std::pair< int, int > > > m_island_boundary_edges
std::vector< std::vector< int > > m_island_tris
std::vector< std::pair< Vec2f, Vec2f > > m_island_raw_bounds
bool m_mesh_dirty = true
bool m_gl_reset_pending = false
std::vector< GLModel > m_island_wireframe
std::vector< GLModel > m_island_fill
GLModel m_stroke_glmodel
GLModel m_dim_quad_glmodel
int m_stencil_bits = -1
GLModel m_tile_outline_glmodel
GLModel m_grid_glmodel
float m_grid_step = 0.f
float m_tiling_scale = 1.f
float m_rotation_deg = 0.f
bool m_tile_enabled = true
bool m_tile_mirrored = false
bool m_snap_enabled = false
std::vector< unsigned char > m_background_pixels
int m_background_width = 0
int m_background_height = 0
bool m_background_dirty = false
bool m_background_quad_dirty = true
GLTexture m_background_texture
GLModel m_background_glmodel
Vec2f m_pan = Vec2f(0.5f, 0.5f)
float m_zoom = 0.75f
bool m_needs_fit = true
Gesture m_gesture = Gesture::None
SelectMode m_select_mode = SelectMode::Island
int m_active_vertex = -1
std::pair< int, int > m_active_edge { -1, -1 }
std::vector< int > m_sel_vertices
std::vector< std::pair< int, int > > m_sel_edges
wxPoint m_cursor_px { 0, 0 }
bool m_cursor_inside = false
bool m_vertex_edit_moved = false
GLModel m_vertex_marker_glmodel
int m_hover_island = -1
int m_hover_vertex = -1
std::pair< int, int > m_hover_edge { -1, -1 }
int m_selected_island = -1
std::vector< int > m_selection
wxPoint m_drag_last_px
Vec2f m_gesture_last_uv = Vec2f::Zero()
float m_gesture_last_angle = 0.f
float m_gesture_last_dist = 0.f
float m_rot_raw_deg = 0.f
float m_rot_applied_deg = 0.f
float m_rot_base_deg = 0.f
float m_rot_display_deg = 0.f
float m_modal_scale_accum = 1.f
GLModel m_dial_glmodel
IslandEditFn m_on_island_edit
UVVertexEditFn m_on_vertex_edit
CommandFn m_on_command
StatusFn m_on_status
PaneState m_pane_state
PaneStateFn m_on_pane_state

Detailed Description


Class Documentation

◆ Slic3r::GUI::UVEditorCanvas::Islands

struct Slic3r::GUI::UVEditorCanvas::Islands
Collaboration diagram for Slic3r::GUI::UVEditorCanvas::Islands:
Class Members
vector< pair< int, int > > boundary_edges
vector< Vec3i32 > indices
int island_count = 0
vector< Vec2f > uvs
vector< int > vertex_island

◆ Slic3r::GUI::UVEditorCanvas::PaneState

struct Slic3r::GUI::UVEditorCanvas::PaneState
Collaboration diagram for Slic3r::GUI::UVEditorCanvas::PaneState:
Class Members
Background background = Background::Height
bool connect_islands = true
size_t face_count = 0
bool has_layer = false
bool has_seams = false
bool has_uv_edits = false
int island_count = 0
wxString layer_name
bool mark_seams = false
float seam_angle_deg = 40.f
bool seam_path = false
int thumbnail_px = 0
vector< unsigned char > thumbnail_rgb
float tile_mm = 0.f
bool unwrap_stale = false
bool unwrapped = false

Member Typedef Documentation

◆ CommandFn

using Slic3r::GUI::UVEditorCanvas::CommandFn = std::function<void(Command, float)>

◆ IslandEditFn

Initial value:
std::function<void(int island, const Vec2f &offset_delta, float rotation_delta, float scale_factor, bool finished)>
Eigen::Matrix< float, 2, 1, Eigen::DontAlign > Vec2f
Definition Point.hpp:64

◆ IslandTransform

using Slic3r::GUI::UVEditorCanvas::IslandTransform = Eigen::Matrix<float, 2, 3>

◆ PaneStateFn

using Slic3r::GUI::UVEditorCanvas::PaneStateFn = std::function<void(const PaneState &)>

◆ StatusFn

using Slic3r::GUI::UVEditorCanvas::StatusFn = std::function<void(const wxString &)>

◆ UVVertexEditFn

using Slic3r::GUI::UVEditorCanvas::UVVertexEditFn = std::function<void(const std::vector<std::pair<int, Vec2f>> &edits)>

Member Enumeration Documentation

◆ Background

Enumerator
Height 
Checker 
Distortion 
123{ Height, Checker, Distortion };
@ Height
Definition Types.hpp:97

◆ Command

Enumerator
FrameAll 
ToggleSnap 
AverageScale 
CutSelectedIsland 
ProjectFromView 
JoinSelected 
UnjoinSelected 
Unwrap 
SetSeamAngle 
SetConnectIslands 
SetSelectMode 
SetMarkSeams 
SetSeamPath 
ClearSeams 
ClearUVEdits 
SetBackground 
PickTexture 
PaneClosed 
SelectionChanged 
113 {
114 FrameAll, ToggleSnap, AverageScale, CutSelectedIsland, ProjectFromView, JoinSelected, UnjoinSelected,
115 Unwrap, SetSeamAngle, SetConnectIslands, SetSelectMode, SetMarkSeams, SetSeamPath, ClearSeams, ClearUVEdits,
116 SetBackground, PickTexture, PaneClosed, SelectionChanged
117 };

◆ Gesture

enum class Slic3r::GUI::UVEditorCanvas::Gesture
strongprivate
Enumerator
None 
Pan 
MoveIsland 
RotateIsland 
RotateIslandModal 
ScaleIslandModal 
MoveVertex 
MoveEdge 
320 {
321 None,
322 Pan,
323 MoveIsland,
324 RotateIsland, // right-drag: rotation tracks the mouse, ends when the button is released
325 RotateIslandModal, // 'R': rotation tracks the mouse until a click confirms or Esc cancels
326 ScaleIslandModal, // 'S': likewise, distance from the centre drives the scale
327 MoveVertex, // Vertex mode: drag one unwrapped vertex
328 MoveEdge, // Edge mode: drag both endpoints of one boundary edge
329 };
@ None
Definition SketchEngine.hpp:180

◆ SelectMode

Enumerator
Island 
Vertex 
Edge 
171{ Island, Vertex, Edge };
@ Edge
Definition Measure.hpp:26
Mesh::Vertex_index Vertex
Definition MeshRepair.cpp:84

Constructor & Destructor Documentation

◆ UVEditorCanvas()

Slic3r::GUI::UVEditorCanvas::UVEditorCanvas ( wxWindow * parent)
explicit
183 : wxGLCanvas(parent, wxID_ANY, gl_attrib_list().data(), wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS)
184{
185 // The GL canvas paints its entire surface, so background erasing is unnecessary (and would
186 // otherwise race with our own rendering) - same setup as OpenGLManager::create_wxglcanvas().
187 SetBackgroundStyle(wxBG_STYLE_PAINT);
188
189 // Shares the app's one real GL context (see class comment) rather than creating an
190 // independent one - this is the same call View3D/Preview/AssembleView make in
191 // GUI_Preview.cpp, and is what lets this canvas reuse wxGetApp().get_shader(...) and GLModel.
192 m_context = wxGetApp().init_glcontext(*this);
193
194 Bind(wxEVT_PAINT, &UVEditorCanvas::on_paint, this);
195 Bind(wxEVT_SIZE, &UVEditorCanvas::on_size, this);
196 Bind(wxEVT_LEFT_DOWN, &UVEditorCanvas::on_mouse, this);
197 Bind(wxEVT_LEFT_UP, &UVEditorCanvas::on_mouse, this);
198 Bind(wxEVT_RIGHT_DOWN, &UVEditorCanvas::on_mouse, this);
199 Bind(wxEVT_RIGHT_UP, &UVEditorCanvas::on_mouse, this);
200 Bind(wxEVT_MIDDLE_DOWN, &UVEditorCanvas::on_mouse, this);
201 Bind(wxEVT_MIDDLE_UP, &UVEditorCanvas::on_mouse, this);
202 Bind(wxEVT_MOTION, &UVEditorCanvas::on_mouse, this);
203 Bind(wxEVT_MOUSE_CAPTURE_LOST, &UVEditorCanvas::on_capture_lost, this);
204 Bind(wxEVT_MOUSEWHEEL, &UVEditorCanvas::on_mouse, this);
205 Bind(wxEVT_LEAVE_WINDOW, &UVEditorCanvas::on_leave, this);
206 Bind(wxEVT_KEY_DOWN, &UVEditorCanvas::on_key, this);
207 Bind(wxEVT_ERASE_BACKGROUND, &UVEditorCanvas::on_erase_background, this);
208}
void on_mouse(wxMouseEvent &evt)
Definition UVEditorCanvas.cpp:872
void on_erase_background(wxEraseEvent &evt)
Definition UVEditorCanvas.hpp:208
void on_paint(wxPaintEvent &evt)
Definition UVEditorCanvas.cpp:1320
wxGLContext * m_context
Definition UVEditorCanvas.hpp:265
void on_size(wxSizeEvent &evt)
Definition UVEditorCanvas.cpp:1326
void on_key(wxKeyEvent &evt)
Definition UVEditorCanvas.cpp:802
void on_leave(wxMouseEvent &evt)
Definition UVEditorCanvas.cpp:1343
void on_capture_lost(wxMouseCaptureLostEvent &evt)
Definition UVEditorCanvas.cpp:796
std::vector< int > gl_attrib_list()
Definition UVEditorCanvas.cpp:105

References m_context, on_capture_lost(), on_erase_background(), on_key(), on_leave(), on_mouse(), on_paint(), and on_size().

Here is the call graph for this function:

Member Function Documentation

◆ cancel_gesture()

void Slic3r::GUI::UVEditorCanvas::cancel_gesture ( )
private
765{
766 // Undo what the gesture already applied live, as the Esc path does, and commit nothing.
767 if (m_on_island_edit) {
769 m_on_island_edit(m_selected_island, Vec2f::Zero(), -m_rot_applied_deg, 1.f, false);
771 m_on_island_edit(m_selected_island, Vec2f::Zero(), 0.f, 1.f / m_modal_scale_accum, false);
772 }
773
775 m_rot_raw_deg = 0.f;
776 m_rot_applied_deg = 0.f;
778 m_vertex_edit_moved = false;
779}
float m_rot_raw_deg
Definition UVEditorCanvas.hpp:384
float m_rot_applied_deg
Definition UVEditorCanvas.hpp:385
bool m_vertex_edit_moved
Definition UVEditorCanvas.hpp:359
Gesture m_gesture
Definition UVEditorCanvas.hpp:330
@ None
Definition UVEditorCanvas.hpp:321
@ ScaleIslandModal
Definition UVEditorCanvas.hpp:326
@ RotateIslandModal
Definition UVEditorCanvas.hpp:325
float m_modal_scale_accum
Definition UVEditorCanvas.hpp:391
int m_selected_island
Definition UVEditorCanvas.hpp:366
IslandEditFn m_on_island_edit
Definition UVEditorCanvas.hpp:401

References m_gesture, m_modal_scale_accum, m_on_island_edit, m_rot_applied_deg, m_rot_raw_deg, m_selected_island, m_vertex_edit_moved, None, RotateIslandModal, and ScaleIslandModal.

Referenced by on_capture_lost().

Here is the caller graph for this function:

◆ content_bounds()

void Slic3r::GUI::UVEditorCanvas::content_bounds ( Vec2f & min_uv,
Vec2f & max_uv ) const
private
425{
426 // Always include the texture's first tile, so an unwrap that happens to be tiny, or absent,
427 // still leaves something sensibly framed on screen.
428 min_uv = Vec2f(0.f, 0.f);
429 max_uv = Vec2f(1.f, 1.f);
430 for (size_t i = 0; i < m_islands.uvs.size(); ++i) {
431 const Vec2f uv = island_uv(i);
432 min_uv = min_uv.cwiseMin(uv);
433 max_uv = max_uv.cwiseMax(uv);
434 }
435}
Islands m_islands
Definition UVEditorCanvas.hpp:267
Vec2f island_uv(size_t vertex) const
Definition UVEditorCanvas.cpp:414

References island_uv(), and m_islands.

Referenced by framed_bounds().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ drop_mouse()

void Slic3r::GUI::UVEditorCanvas::drop_mouse ( )
private
788{
789 if (HasCapture())
790 ReleaseMouse();
791}

Referenced by end_gesture(), and on_mouse().

Here is the caller graph for this function:

◆ edge_at()

std::pair< int, int > Slic3r::GUI::UVEditorCanvas::edge_at ( const Vec2f & uv) const
private
592{
593 const wxSize size = GetSize();
594 const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight())));
595 const float threshold = SNAP_PIXELS * uv_per_px;
596
597 // Point-to-segment distance in texture-UV space, against the island outlines (the edges the pane
598 // exists to show). Interior edges are left alone: the boundary is what a user reshapes.
599 const auto seg_dist_sq = [](const Vec2f &p, const Vec2f &a, const Vec2f &b) {
600 const Vec2f ab = b - a;
601 const float l2 = ab.squaredNorm();
602 const float t = (l2 > 1e-12f) ? std::clamp((p - a).dot(ab) / l2, 0.f, 1.f) : 0.f;
603 return (p - (a + ab * t)).squaredNorm();
604 };
605 std::pair<int, int> best{ -1, -1 };
606 float best_d = threshold * threshold;
607 for (const auto &[a, b] : m_islands.boundary_edges) {
608 if (a < 0 || b < 0 || size_t(a) >= m_islands.uvs.size() || size_t(b) >= m_islands.uvs.size())
609 continue;
610 const float d = seg_dist_sq(uv, island_uv(size_t(a)), island_uv(size_t(b)));
611 if (d < best_d) {
612 best_d = d;
613 best = { a, b };
614 }
615 }
616 return best;
617}
float m_zoom
Definition UVEditorCanvas.hpp:316
constexpr float SNAP_PIXELS
Definition UVEditorCanvas.cpp:77
T l2(const boost::geometry::model::d2::point_xy< T > &v)
Definition ExtrusionSimulator.cpp:177
T dot(const boost::geometry::model::d2::point_xy< T > &v1, const boost::geometry::model::d2::point_xy< T > &v2)
Definition ExtrusionSimulator.cpp:154

References Slic3r::dot(), island_uv(), Slic3r::l2(), m_islands, and m_zoom.

Referenced by on_mouse(), and update_hover().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ end_gesture()

void Slic3r::GUI::UVEditorCanvas::end_gesture ( )
private
712{
713 const bool was_editing = m_gesture == Gesture::MoveIsland || m_gesture == Gesture::RotateIsland ||
715
716 // Stick to a neighbour at the end of a move, rather than magnetically fighting the cursor
717 // throughout it - a snap that keeps re-applying mid-drag is very hard to pull *out* of.
719 const Vec2f correction = snap_correction(m_selected_island);
720 if (!correction.isZero())
721 m_on_island_edit(m_selected_island, uv_delta_to_unwrap(correction), 0.f, 1.f, false);
722 }
723
724 if (was_editing && m_on_island_edit)
725 m_on_island_edit(m_selected_island, Vec2f::Zero(), 0.f, 1.f, /* finished */ true);
726
727 // Commit a vertex/edge edit once, on release: hand the owner the affected unwrapped vertices and
728 // their new raw-unwrap coordinates so it can store the overrides and re-solve the bake preview.
731 std::vector<std::pair<int, Vec2f>> edits;
732 const auto add = [&](int v) {
733 if (v >= 0 && size_t(v) < m_islands.uvs.size())
734 edits.emplace_back(v, m_islands.uvs[size_t(v)]);
735 };
736 // Commit every element of the multi-selection, not just the primary, so a group drag stores all
737 // the moved vertices' overrides. Falls back to the primary if the selection is somehow empty.
739 if (m_sel_vertices.empty())
741 else
742 for (const int v : m_sel_vertices)
743 add(v);
744 } else {
745 const std::vector<int> endpoints = selected_edge_endpoints();
746 if (endpoints.empty()) {
747 add(m_active_edge.first);
748 add(m_active_edge.second);
749 } else
750 for (const int v : endpoints)
751 add(v);
752 }
753 if (!edits.empty())
754 m_on_vertex_edit(edits);
755 }
756
758 m_rot_raw_deg = 0.f;
759 m_rot_applied_deg = 0.f;
761 drop_mouse();
762}
Vec2f snap_correction(int island) const
Definition UVEditorCanvas.cpp:671
UVVertexEditFn m_on_vertex_edit
Definition UVEditorCanvas.hpp:402
std::pair< int, int > m_active_edge
Definition UVEditorCanvas.hpp:336
std::vector< int > selected_edge_endpoints() const
Definition UVEditorCanvas.cpp:701
int m_active_vertex
Definition UVEditorCanvas.hpp:335
void drop_mouse()
Definition UVEditorCanvas.cpp:787
@ RotateIsland
Definition UVEditorCanvas.hpp:324
@ MoveEdge
Definition UVEditorCanvas.hpp:328
@ MoveVertex
Definition UVEditorCanvas.hpp:327
@ MoveIsland
Definition UVEditorCanvas.hpp:323
std::vector< int > m_sel_vertices
Definition UVEditorCanvas.hpp:340
Vec2f uv_delta_to_unwrap(const Vec2f &delta_uv) const
Definition UVEditorCanvas.cpp:662
SpiralVase::SpiralPoint add(SpiralVase::SpiralPoint a, SpiralVase::SpiralPoint b)
Definition SpiralVase.cpp:26

References drop_mouse(), m_active_edge, m_active_vertex, m_gesture, m_islands, m_modal_scale_accum, m_on_island_edit, m_on_vertex_edit, m_rot_applied_deg, m_rot_raw_deg, m_sel_vertices, m_selected_island, m_vertex_edit_moved, MoveEdge, MoveIsland, MoveVertex, None, RotateIsland, RotateIslandModal, ScaleIslandModal, selected_edge_endpoints(), snap_correction(), and uv_delta_to_unwrap().

Referenced by on_key(), and on_mouse().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ fit_view_to_content()

void Slic3r::GUI::UVEditorCanvas::fit_view_to_content ( )
private
452{
453 Vec2f min_uv, max_uv;
454 framed_bounds(min_uv, max_uv);
455
456 const wxSize size = GetSize();
457 const float aspect = float(std::max(1, size.GetWidth())) / float(std::max(1, size.GetHeight()));
458 const Vec2f half = 0.5f * (max_uv - min_uv);
459
460 m_pan = 0.5f * (min_uv + max_uv);
461 // m_zoom is the half-extent shown across the *shorter* pane edge (see view_half_extents()), so
462 // each axis' required half-extent has to be converted back into that unit before the larger of
463 // the two is taken. Sizing off the bigger axis alone, as this did, ignores the pane's shape and
464 // zooms out further than either axis needs on anything but a square pane. The 1.1 leaves a
465 // margin so the outermost island edge is not flush against the frame.
466 m_zoom = std::max(1.1f * std::max(half.x() / std::max(aspect, 1.f), half.y() * std::min(aspect, 1.f)),
467 0.05f);
468 m_needs_fit = false;
469}
bool m_needs_fit
Definition UVEditorCanvas.hpp:317
Vec2f m_pan
Definition UVEditorCanvas.hpp:315
void framed_bounds(Vec2f &min_uv, Vec2f &max_uv) const
Definition UVEditorCanvas.cpp:437

References framed_bounds(), m_needs_fit, m_pan, and m_zoom.

Referenced by render().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ framed_bounds()

void Slic3r::GUI::UVEditorCanvas::framed_bounds ( Vec2f & min_uv,
Vec2f & max_uv ) const
private
438{
439 content_bounds(min_uv, max_uv);
440 // With tiling on, the backdrop is snapped out to whole tiles, so it is bigger than the raw
441 // bounds - and by a different amount on each side. Framing the raw bounds therefore left that
442 // backdrop visibly off-centre: hanging past one edge of the pane with dead space against the
443 // other. Frame what is drawn instead. (Tiling off draws only the first tile, which the bounds
444 // already contain, so there is nothing to snap.)
445 if (m_tile_enabled) {
446 min_uv = Vec2f(std::floor(min_uv.x()), std::floor(min_uv.y()));
447 max_uv = Vec2f(std::ceil(max_uv.x()), std::ceil(max_uv.y()));
448 }
449}
bool m_tile_enabled
Definition UVEditorCanvas.hpp:297
void content_bounds(Vec2f &min_uv, Vec2f &max_uv) const
Definition UVEditorCanvas.cpp:424

References content_bounds(), and m_tile_enabled.

Referenced by fit_view_to_content(), and rebuild_background_quad().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ grab_mouse()

void Slic3r::GUI::UVEditorCanvas::grab_mouse ( )
private
782{
783 if (!HasCapture())
784 CaptureMouse();
785}

Referenced by on_mouse().

Here is the caller graph for this function:

◆ has_islands()

bool Slic3r::GUI::UVEditorCanvas::has_islands ( ) const
inline
186{ return m_islands.island_count > 0 && !m_islands.indices.empty(); }

References m_islands.

Referenced by render(), update_hover(), and update_status().

Here is the caller graph for this function:

◆ invalidate_gl()

void Slic3r::GUI::UVEditorCanvas::invalidate_gl ( )
inline
196 {
197 m_gl_reset_pending = true;
198 Refresh();
199 }
bool m_gl_reset_pending
Definition UVEditorCanvas.hpp:281

References m_gl_reset_pending.

◆ is_edge_selected()

bool Slic3r::GUI::UVEditorCanvas::is_edge_selected ( const std::pair< int, int > & e) const
inlineprivate
347 {
348 return std::find(m_sel_edges.begin(), m_sel_edges.end(), e) != m_sel_edges.end();
349 }
std::vector< std::pair< int, int > > m_sel_edges
Definition UVEditorCanvas.hpp:341

References m_sel_edges.

Referenced by on_mouse(), and render().

Here is the caller graph for this function:

◆ is_selected()

bool Slic3r::GUI::UVEditorCanvas::is_selected ( int island) const
inlineprivate
371 {
372 return std::find(m_selection.begin(), m_selection.end(), island) != m_selection.end();
373 }
std::vector< int > m_selection
Definition UVEditorCanvas.hpp:369

References m_selection.

Referenced by island_at(), on_mouse(), and render().

Here is the caller graph for this function:

◆ is_vertex_selected()

bool Slic3r::GUI::UVEditorCanvas::is_vertex_selected ( int v) const
inlineprivate
343 {
344 return std::find(m_sel_vertices.begin(), m_sel_vertices.end(), v) != m_sel_vertices.end();
345 }

References m_sel_vertices.

Referenced by on_mouse(), and render().

Here is the caller graph for this function:

◆ island_at()

int Slic3r::GUI::UVEditorCanvas::island_at ( const Vec2f & uv) const
private
502{
503 int found = -1;
504 for (int island = 0; island < int(m_island_tris.size()); ++island) {
505 // The raw bounds through the island's affine: the box of the four transformed corners contains the island.
506 const auto &[lo, hi] = m_island_raw_bounds[size_t(island)];
507 if (lo.x() > hi.x())
508 continue;
509 const IslandTransform m = size_t(island) < m_transforms.size() ? m_transforms[size_t(island)] :
510 IslandTransform(IslandTransform::Identity());
511 Vec2f bmin = Vec2f::Constant(std::numeric_limits<float>::max()), bmax = -bmin;
512 for (const Vec2f &corner : { lo, hi, Vec2f(lo.x(), hi.y()), Vec2f(hi.x(), lo.y()) }) {
513 const Vec2f p = m.block<2, 2>(0, 0) * corner + m.col(2);
514 bmin = bmin.cwiseMin(p);
515 bmax = bmax.cwiseMax(p);
516 }
517 if (uv.x() < bmin.x() || uv.y() < bmin.y() || uv.x() > bmax.x() || uv.y() > bmax.y())
518 continue;
519
520 for (const int t : m_island_tris[size_t(island)]) {
521 const Vec3i32 &tri = m_islands.indices[size_t(t)];
522 if (!point_in_triangle(uv, island_uv(size_t(tri[0])), island_uv(size_t(tri[1])), island_uv(size_t(tri[2]))))
523 continue;
524 // Islands are allowed to overlap, so a point can be inside several. Keep whichever is already
525 // selected - otherwise a drag of a partly-covered island would be stolen mid-gesture by the
526 // one on top of it.
527 if (is_selected(island))
528 return island;
529 found = island;
530 break;
531 }
532 }
533 return found;
534}
bool is_selected(int island) const
Definition UVEditorCanvas.hpp:370
std::vector< std::vector< int > > m_island_tris
Definition UVEditorCanvas.hpp:277
Eigen::Matrix< float, 2, 3 > IslandTransform
Definition UVEditorCanvas.hpp:66
std::vector< IslandTransform > m_transforms
Definition UVEditorCanvas.hpp:268
std::vector< std::pair< Vec2f, Vec2f > > m_island_raw_bounds
Definition UVEditorCanvas.hpp:278
bool point_in_triangle(const Vec2f &p, const Vec2f &a, const Vec2f &b, const Vec2f &c)
Definition UVEditorCanvas.cpp:150
Eigen::Matrix< int32_t, 3, 1, Eigen::DontAlign > Vec3i32
Definition Point.hpp:59

References is_selected(), island_uv(), m_island_raw_bounds, m_island_tris, m_islands, and m_transforms.

Referenced by on_mouse(), and update_hover().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ island_centroid()

Vec2f Slic3r::GUI::UVEditorCanvas::island_centroid ( int island) const
private
651{
652 Vec2f sum = Vec2f::Zero();
653 int count = 0;
654 for (size_t i = 0; i < m_islands.uvs.size(); ++i)
655 if (m_islands.vertex_island[i] == island) {
656 sum += island_uv(i);
657 ++count;
658 }
659 return (count > 0) ? Vec2f(sum / float(count)) : Vec2f::Zero();
660}
static unsigned int count
Definition AuxiliaryDataViewModel.cpp:246

References count, island_uv(), and m_islands.

Referenced by on_key(), on_mouse(), and rebuild_rotation_dial().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ island_rotation_deg()

float Slic3r::GUI::UVEditorCanvas::island_rotation_deg ( int island) const
private
641{
642 // The rotation baked into the island's affine, in the same y-down UV convention the gesture uses.
643 // First column is scale*(cos, sin); its angle is the island's on-screen orientation.
644 if (island < 0 || size_t(island) >= m_transforms.size())
645 return 0.f;
646 const IslandTransform &m = m_transforms[size_t(island)];
647 return std::atan2(m(1, 0), m(0, 0)) * 180.f / float(M_PI);
648}
#define M_PI
Definition ExtrusionSimulator.cpp:31

References M_PI, and m_transforms.

Referenced by on_key(), and on_mouse().

Here is the caller graph for this function:

◆ island_uv()

Vec2f Slic3r::GUI::UVEditorCanvas::island_uv ( size_t vertex) const
private
415{
416 const Vec2f &raw = m_islands.uvs[vertex];
417 const int island = m_islands.vertex_island[vertex];
418 if (island < 0 || size_t(island) >= m_transforms.size())
419 return raw;
420 const IslandTransform &m = m_transforms[size_t(island)];
421 return m.block<2, 2>(0, 0) * raw + m.col(2);
422}

References m_islands, and m_transforms.

Referenced by content_bounds(), edge_at(), island_at(), island_centroid(), render(), snap_correction(), and vertex_at().

Here is the caller graph for this function:

◆ move_vertex_raw()

void Slic3r::GUI::UVEditorCanvas::move_vertex_raw ( int unwrapped_vertex,
const Vec2f & delta_uv )
private
620{
621 if (v < 0 || size_t(v) >= m_islands.uvs.size())
622 return;
623 const int island = m_islands.vertex_island[size_t(v)];
624 // The gesture happens in texture-UV space; the stored coordinate is the raw unwrap. Undo the
625 // island's own linear map (which includes the layer's tiling scale + rotation) to get there.
626 Eigen::Matrix2f lin = Eigen::Matrix2f::Identity();
627 if (island >= 0 && size_t(island) < m_transforms.size())
628 lin = m_transforms[size_t(island)].block<2, 2>(0, 0);
629 const float det = lin.determinant();
630 const Vec2f raw_delta = (std::abs(det) > 1e-12f) ? Vec2f(lin.inverse() * delta_uv) : delta_uv;
631 m_islands.uvs[size_t(v)] += raw_delta;
632 if (island >= 0 && size_t(island) < m_island_raw_bounds.size()) { // keep the pick box around the moved vertex
633 auto &[lo, hi] = m_island_raw_bounds[size_t(island)];
634 lo = lo.cwiseMin(m_islands.uvs[size_t(v)]);
635 hi = hi.cwiseMax(m_islands.uvs[size_t(v)]);
636 }
637 m_mesh_dirty = true; // the edited raw uv is redrawn from rebuild_island_models() next frame
638}
bool m_mesh_dirty
Definition UVEditorCanvas.hpp:280

References m_island_raw_bounds, m_islands, m_mesh_dirty, and m_transforms.

Referenced by on_mouse().

Here is the caller graph for this function:

◆ on_capture_lost()

void Slic3r::GUI::UVEditorCanvas::on_capture_lost ( wxMouseCaptureLostEvent & evt)
private
797{
799 Refresh();
800}
void cancel_gesture()
Definition UVEditorCanvas.cpp:764

References cancel_gesture().

Referenced by UVEditorCanvas().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_erase_background()

void Slic3r::GUI::UVEditorCanvas::on_erase_background ( wxEraseEvent & evt)
inlineprivate
208{} // required to avoid flicker on MSW, deliberately a no-op

Referenced by UVEditorCanvas().

Here is the caller graph for this function:

◆ on_key()

void Slic3r::GUI::UVEditorCanvas::on_key ( wxKeyEvent & evt)
private
803{
804 const int key = evt.GetKeyCode();
805
806 // Undo/redo while the pane has focus. Island edits already take a Plater snapshot per gesture (see
807 // GLGizmoTextureDisplacement::on_island_edited), so this just drives the same global history; the
808 // gizmo re-pushes the restored island transforms into the canvas on the reload that follows.
809 if (evt.ControlDown() && (key == 'Z' || key == 'z')) {
810 if (evt.ShiftDown()) wxGetApp().plater()->redo();
811 else wxGetApp().plater()->undo();
812 return;
813 }
814 if (evt.ControlDown() && (key == 'Y' || key == 'y')) {
815 wxGetApp().plater()->redo();
816 return;
817 }
818
820 if (key == WXK_ESCAPE) {
821 // Put the island back exactly where the modal gesture found it, then finish.
822 if (m_on_island_edit) {
824 m_on_island_edit(m_selected_island, Vec2f::Zero(), -m_rot_applied_deg, 1.f, false);
826 m_on_island_edit(m_selected_island, Vec2f::Zero(), 0.f, 1.f / m_modal_scale_accum, false);
827 }
828 end_gesture();
829 Refresh();
830 return;
831 }
832 if (key == WXK_RETURN || key == WXK_NUMPAD_ENTER) {
833 end_gesture();
834 Refresh();
835 return;
836 }
837 }
838
839 // Frame everything. The unwrap is packed in mm and then divided by the layer's tile size, so it
840 // can easily sit tens of tiles away from the texture's first one - panning back by hand from
841 // there is hopeless, and without this there would be no way to reach reset_view() at all.
842 if (key == WXK_HOME || key == 'F' || key == 'f') {
843 reset_view();
844 return;
845 }
846
847 // Blender's modal transforms: R / S, then the transform follows the mouse until it is confirmed
848 // with a click or Enter, or abandoned with Esc.
850 (key == 'R' || key == 'r' || key == 'S' || key == 's')) {
851 const Vec2f uv = screen_to_uv(ScreenToClient(wxGetMousePosition()));
853 const Vec2f rel = uv - centre;
854 if (key == 'R' || key == 'r') {
856 m_rot_raw_deg = 0.f;
857 m_rot_applied_deg = 0.f;
860 m_gesture_last_angle = std::atan2(rel.y(), rel.x());
861 } else {
864 m_gesture_last_dist = std::max(rel.norm(), 1e-6f);
865 }
866 return;
867 }
868
869 evt.Skip();
870}
float m_rot_display_deg
Definition UVEditorCanvas.hpp:390
@ Island
Definition UVEditorCanvas.hpp:171
float m_rot_base_deg
Definition UVEditorCanvas.hpp:389
Vec2f screen_to_uv(const wxPoint &px) const
Definition UVEditorCanvas.cpp:485
float m_gesture_last_dist
Definition UVEditorCanvas.hpp:377
SelectMode m_select_mode
Definition UVEditorCanvas.hpp:332
void reset_view()
Definition UVEditorCanvas.cpp:330
float island_rotation_deg(int island) const
Definition UVEditorCanvas.cpp:640
float m_gesture_last_angle
Definition UVEditorCanvas.hpp:376
void end_gesture()
Definition UVEditorCanvas.cpp:711
Vec2f island_centroid(int island) const
Definition UVEditorCanvas.cpp:650

References end_gesture(), Island, island_centroid(), island_rotation_deg(), m_gesture, m_gesture_last_angle, m_gesture_last_dist, m_modal_scale_accum, m_on_island_edit, m_rot_applied_deg, m_rot_base_deg, m_rot_display_deg, m_rot_raw_deg, m_select_mode, m_selected_island, None, reset_view(), RotateIslandModal, ScaleIslandModal, and screen_to_uv().

Referenced by UVEditorCanvas().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_leave()

void Slic3r::GUI::UVEditorCanvas::on_leave ( wxMouseEvent & evt)
private
1344{
1345 evt.Skip();
1346 if (m_cursor_inside) {
1347 m_cursor_inside = false;
1348 // The +/- hint and the hover highlight were following the cursor; drop them now the pointer is gone.
1349 if (m_gesture == Gesture::None) {
1351 m_hover_edge = { -1, -1 };
1352 }
1353 Refresh();
1354 }
1355}
bool m_cursor_inside
Definition UVEditorCanvas.hpp:356
int m_hover_vertex
Definition UVEditorCanvas.hpp:364
int m_hover_island
Definition UVEditorCanvas.hpp:363
std::pair< int, int > m_hover_edge
Definition UVEditorCanvas.hpp:365

References m_cursor_inside, m_gesture, m_hover_edge, m_hover_island, m_hover_vertex, None, and Slic3r::GUI::wxMouseEvent.

Referenced by UVEditorCanvas().

Here is the caller graph for this function:

◆ on_mouse()

void Slic3r::GUI::UVEditorCanvas::on_mouse ( wxMouseEvent & evt)
private
873{
874 const wxEventType type = evt.GetEventType();
875 const wxPoint pos = evt.GetPosition();
876
877 // Track the pointer so the +/- add-remove hint can be drawn next to it in Vertex/Edge mode.
879 m_cursor_inside = true;
880 if (type == wxEVT_MOTION && m_gesture == Gesture::None) {
881 update_hover(pos);
883 Refresh(); // animate the hint (and its +/- flip) as the pointer/modifiers move
884 }
885
886 // Key events (R/S/Home) only arrive if this canvas has focus, and clicking it is the natural way
887 // to ask for it - the pane is not in the tab order.
888 if (type == wxEVT_LEFT_DOWN || type == wxEVT_RIGHT_DOWN || type == wxEVT_MIDDLE_DOWN)
889 SetFocus();
890
891 // A modal R/S is confirmed by any click, exactly as in Blender.
893 (type == wxEVT_LEFT_DOWN || type == wxEVT_RIGHT_DOWN)) {
894 end_gesture();
895 Refresh();
896 return;
897 }
898
899 if (type == wxEVT_LEFT_DOWN) {
900 const Vec2f uv = screen_to_uv(pos);
904 const int hit = vertex_at(uv);
905 m_active_edge = { -1, -1 };
906 m_vertex_edit_moved = false;
907 // Same Shift-adds / Ctrl-toggles / plain-replaces rules as island selection, so a group of
908 // vertices can be picked and dragged together.
909 if (hit >= 0) {
910 if (evt.ShiftDown()) {
911 if (!is_vertex_selected(hit))
912 m_sel_vertices.push_back(hit);
913 } else if (evt.ControlDown()) {
914 if (auto it = std::find(m_sel_vertices.begin(), m_sel_vertices.end(), hit); it != m_sel_vertices.end())
915 m_sel_vertices.erase(it);
916 else
917 m_sel_vertices.push_back(hit);
918 } else if (!is_vertex_selected(hit)) {
919 m_sel_vertices.assign(1, hit);
920 }
921 } else if (!evt.ShiftDown() && !evt.ControlDown()) {
922 m_sel_vertices.clear();
923 }
924 // Primary = the clicked vertex only if it is (still) selected; a Ctrl-deselect just toggles.
925 m_active_vertex = (hit >= 0 && is_vertex_selected(hit)) ? hit : -1;
927 } else if (m_select_mode == SelectMode::Edge) {
928 const std::pair<int, int> hit = edge_at(uv);
929 m_active_vertex = -1;
930 m_vertex_edit_moved = false;
931 if (hit.first >= 0) {
932 if (evt.ShiftDown()) {
933 if (!is_edge_selected(hit))
934 m_sel_edges.push_back(hit);
935 } else if (evt.ControlDown()) {
936 if (auto it = std::find(m_sel_edges.begin(), m_sel_edges.end(), hit); it != m_sel_edges.end())
937 m_sel_edges.erase(it);
938 else
939 m_sel_edges.push_back(hit);
940 } else if (!is_edge_selected(hit)) {
941 m_sel_edges.assign(1, hit);
942 }
943 } else if (!evt.ShiftDown() && !evt.ControlDown()) {
944 m_sel_edges.clear();
945 }
946 m_active_edge = (hit.first >= 0 && is_edge_selected(hit)) ? hit : std::pair<int, int>{ -1, -1 };
948 } else {
949 const std::vector<int> selection_before = m_selection;
950 const int hit = island_at(uv);
951 if (hit >= 0) {
952 if (evt.ShiftDown()) {
953 // Shift adds to the selection (and makes the clicked one the new primary).
954 if (!is_selected(hit))
955 m_selection.push_back(hit);
956 m_selected_island = hit;
957 } else if (evt.ControlDown()) {
958 // Ctrl toggles: clicking a selected island removes it (the requested "deselect"),
959 // clicking an unselected one adds it.
960 if (auto it = std::find(m_selection.begin(), m_selection.end(), hit); it != m_selection.end()) {
961 m_selection.erase(it);
962 m_selected_island = m_selection.empty() ? -1 : m_selection.back();
963 } else {
964 m_selection.push_back(hit);
965 m_selected_island = hit;
966 }
967 } else {
968 // Plain click: keep the whole selection if the clicked island is already part of it (so
969 // a drag moves the group), otherwise collapse to just this one.
970 if (!is_selected(hit))
971 m_selection.assign(1, hit);
972 m_selected_island = hit;
973 }
974 } else if (!evt.ShiftDown() && !evt.ControlDown()) {
975 // Clicking empty space with no modifier clears the selection and pans.
976 m_selection.clear();
978 }
980 // The owner shows the selection on the model and would not hear of this click otherwise in time: a
981 // click that clears the selection starts no gesture, and one that sets it reports only on release.
982 if (m_selection != selection_before)
984 }
985 grab_mouse();
986 Refresh();
987 } else if (type == wxEVT_RIGHT_DOWN && m_selected_island >= 0 && m_select_mode == SelectMode::Island) {
990 m_rot_raw_deg = 0.f;
991 m_rot_applied_deg = 0.f;
994 m_gesture_last_angle = std::atan2(rel.y(), rel.x());
995 grab_mouse();
996 } else if (type == wxEVT_MIDDLE_DOWN) {
999 grab_mouse();
1000 } else if (type == wxEVT_LEFT_UP || type == wxEVT_RIGHT_UP || type == wxEVT_MIDDLE_UP) {
1001 // The drag is over either way. A modal R/S keeps running until a click confirms it, but it
1002 // tracks the pointer over this canvas and needs no capture to do so, so the capture goes back
1003 // here rather than waiting for that click - which may never come.
1004 drop_mouse();
1006 end_gesture();
1007 Refresh();
1008 }
1009 } else if (type == wxEVT_MOTION) {
1010 switch (m_gesture) {
1011 case Gesture::Pan: {
1012 const wxSize size = GetSize();
1013 const float scale = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight())));
1014 // Both axes point the same way on screen as in UV space (v runs down), so dragging the
1015 // content along with the cursor is a subtraction on both.
1016 m_pan.x() -= float(pos.x - m_drag_last_px.x) * scale;
1017 m_pan.y() -= float(pos.y - m_drag_last_px.y) * scale;
1019 Refresh();
1020 break;
1021 }
1022 case Gesture::MoveIsland: {
1023 const Vec2f uv = screen_to_uv(pos);
1024 if (m_on_island_edit)
1026 m_gesture_last_uv = uv;
1027 Refresh();
1028 break;
1029 }
1030 case Gesture::MoveVertex: {
1031 const Vec2f uv = screen_to_uv(pos);
1032 const Vec2f delta = uv - m_gesture_last_uv;
1033 // Move the whole selection by the same uv-space delta (each vertex converts it through its
1034 // own island transform in move_vertex_raw). Falls back to the primary if none is selected.
1035 if (m_sel_vertices.empty())
1037 else
1038 for (const int v : m_sel_vertices)
1039 move_vertex_raw(v, delta);
1040 m_gesture_last_uv = uv;
1041 m_vertex_edit_moved = true;
1042 Refresh();
1043 break;
1044 }
1045 case Gesture::MoveEdge: {
1046 const Vec2f uv = screen_to_uv(pos);
1047 const Vec2f delta = uv - m_gesture_last_uv;
1048 // Move every unique endpoint of every selected edge once. Falls back to the primary edge.
1049 const std::vector<int> endpoints = selected_edge_endpoints();
1050 if (endpoints.empty()) {
1051 move_vertex_raw(m_active_edge.first, delta);
1052 move_vertex_raw(m_active_edge.second, delta);
1053 } else
1054 for (const int v : endpoints)
1055 move_vertex_raw(v, delta);
1056 m_gesture_last_uv = uv;
1057 m_vertex_edit_moved = true;
1058 Refresh();
1059 break;
1060 }
1064 const float angle = std::atan2(rel.y(), rel.x());
1065 // Accumulate the raw mouse rotation incrementally so it survives crossing +/-180 degrees.
1068
1069 // With Shift, quantise to *global* 15-degree marks (0/15/30...), i.e. snap the island's
1070 // absolute on-screen orientation, not 15 degrees relative to wherever it started (#10) -
1071 // snapping the target rather than each delta is what keeps it from juddering on a step.
1072 constexpr float STEP = 15.f;
1073 const float absolute = m_rot_base_deg + m_rot_raw_deg;
1074 const float target_abs = evt.ShiftDown() ? std::round(absolute / STEP) * STEP : absolute;
1075 const float deg = target_abs - (m_rot_base_deg + m_rot_applied_deg);
1076 if (m_on_island_edit && deg != 0.f)
1077 m_on_island_edit(m_selected_island, Vec2f::Zero(), deg, 1.f, false);
1078 m_rot_applied_deg = target_abs - m_rot_base_deg;
1079 m_rot_display_deg = target_abs;
1080 Refresh();
1081 break;
1082 }
1085 const float dist = std::max(rel.norm(), 1e-6f);
1086 const float factor = dist / m_gesture_last_dist;
1087 if (m_on_island_edit && factor != 1.f)
1088 m_on_island_edit(m_selected_island, Vec2f::Zero(), 0.f, factor, false);
1089 m_modal_scale_accum *= factor;
1091 Refresh();
1092 break;
1093 }
1094 default: break;
1095 }
1096 } else if (type == wxEVT_MOUSEWHEEL) {
1097 // Zoom about the cursor, not the view centre - otherwise zooming in on something off to the
1098 // side walks it straight out of the frame.
1099 const Vec2f before = screen_to_uv(pos);
1100 const float factor = std::pow(0.9f, float(evt.GetWheelRotation()) / float(evt.GetWheelDelta()));
1101 m_zoom = std::clamp(m_zoom * factor, 0.001f, 5000.f);
1102 const Vec2f after = screen_to_uv(pos);
1103 m_pan += before - after;
1104 if (m_gesture == Gesture::None)
1105 update_hover(pos);
1106 Refresh();
1107 }
1108}
int scale(const int val)
Definition WipeTowerDialog.cpp:58
@ Vertex
Definition UVEditorCanvas.hpp:171
@ Edge
Definition UVEditorCanvas.hpp:171
wxPoint m_cursor_px
Definition UVEditorCanvas.hpp:355
int island_at(const Vec2f &uv) const
Definition UVEditorCanvas.cpp:501
@ SelectionChanged
Definition UVEditorCanvas.hpp:116
void grab_mouse()
Definition UVEditorCanvas.cpp:781
int vertex_at(const Vec2f &uv) const
Definition UVEditorCanvas.cpp:572
Vec2f m_gesture_last_uv
Definition UVEditorCanvas.hpp:375
void move_vertex_raw(int unwrapped_vertex, const Vec2f &delta_uv)
Definition UVEditorCanvas.cpp:619
void update_hover(const wxPoint &pos)
Definition UVEditorCanvas.cpp:536
bool is_edge_selected(const std::pair< int, int > &e) const
Definition UVEditorCanvas.hpp:346
wxPoint m_drag_last_px
Definition UVEditorCanvas.hpp:374
bool is_vertex_selected(int v) const
Definition UVEditorCanvas.hpp:342
void run_command(Command cmd, float value=0.f)
Definition UVEditorCanvas.cpp:336
Gesture
Definition UVEditorCanvas.hpp:320
@ Pan
Definition UVEditorCanvas.hpp:322
std::pair< int, int > edge_at(const Vec2f &uv) const
Definition UVEditorCanvas.cpp:591
float angle_delta(float from, float to)
Definition UVEditorCanvas.cpp:162
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:138

References Slic3r::angle(), Slic3r::Geometry::dist(), drop_mouse(), Edge, edge_at(), end_gesture(), grab_mouse(), is_edge_selected(), is_selected(), is_vertex_selected(), Island, island_at(), island_centroid(), island_rotation_deg(), m_active_edge, m_active_vertex, m_cursor_inside, m_cursor_px, m_drag_last_px, m_gesture, m_gesture_last_angle, m_gesture_last_dist, m_gesture_last_uv, m_modal_scale_accum, m_on_island_edit, m_pan, M_PI, m_rot_applied_deg, m_rot_base_deg, m_rot_display_deg, m_rot_raw_deg, m_sel_edges, m_sel_vertices, m_select_mode, m_selected_island, m_selection, m_vertex_edit_moved, m_zoom, move_vertex_raw(), MoveEdge, MoveIsland, MoveVertex, None, Pan, RotateIsland, RotateIslandModal, run_command(), scale(), ScaleIslandModal, screen_to_uv(), selected_edge_endpoints(), SelectionChanged, update_hover(), uv_delta_to_unwrap(), Vertex, vertex_at(), and Slic3r::GUI::wxMouseEvent.

Referenced by UVEditorCanvas().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_paint()

void Slic3r::GUI::UVEditorCanvas::on_paint ( wxPaintEvent & evt)
private
1321{
1322 wxPaintDC dc(this);
1323 render();
1324}
void render()
Definition UVEditorCanvas.cpp:1357

References render().

Referenced by UVEditorCanvas().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ on_size()

void Slic3r::GUI::UVEditorCanvas::on_size ( wxSizeEvent & evt)
private
1327{
1328 evt.Skip();
1329 // Refresh() alone only *schedules* a repaint, which Windows can coalesce/delay until a live
1330 // resize drag ends, leaving stale wrong-aspect-ratio content on screen throughout the drag.
1331 // Update() flushes it immediately - still through the normal paint path (unlike calling
1332 // render() directly), which matters because this canvas shares the app's one GL context with
1333 // the 3D view and must not hijack it outside its own paint.
1334 // Not while a 3D canvas frame is in progress, though: wxGTK delivers this size event synchronously
1335 // when the texture displacement gizmo relays out the pane from its ImGui panel, i.e. from inside
1336 // that frame, and flushing the paint there would take the shared context away from it. The Refresh()
1337 // then runs the paint once the frame is over.
1338 Refresh();
1340 Update();
1341}
static bool is_frame_in_progress()
Definition GLCanvas3D.hpp:989

References Slic3r::GUI::GLCanvas3D::is_frame_in_progress().

Referenced by UVEditorCanvas().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ pane_state()

const PaneState & Slic3r::GUI::UVEditorCanvas::pane_state ( ) const
inline
148{ return m_pane_state; }
PaneState m_pane_state
Definition UVEditorCanvas.hpp:405

References m_pane_state.

◆ rebuild_background_quad()

void Slic3r::GUI::UVEditorCanvas::rebuild_background_quad ( )
private
1273{
1274 m_background_glmodel.reset();
1276 if (m_background_width <= 0 || m_background_height <= 0)
1277 return;
1278
1279 Vec2f lo, hi;
1280 if (m_tile_enabled) {
1281 // Whole tiles, so the backdrop's edge lands on a tile boundary instead of slicing a brick in
1282 // half. framed_bounds() applies exactly this, and the view is framed on its result - the two
1283 // must not drift apart or the backdrop stops being centred in the pane.
1284 framed_bounds(lo, hi);
1285 } else {
1286 // Tiling off: the sampler reads 0 outside the first tile and nothing else exists, so the
1287 // backdrop is exactly that one tile (GL_CLAMP_TO_BORDER in render() gives it the same
1288 // black surround, rather than smearing the edge texels outward).
1289 lo = Vec2f(0.f, 0.f);
1290 hi = Vec2f(1.f, 1.f);
1291 }
1292
1293 GLModel::Geometry init_data;
1295 init_data.reserve_vertices(4);
1296 init_data.reserve_indices(6);
1297 // Texcoord == position: the height sampling maps one whole texture onto each unit square of uv
1298 // (see DecodedHeightTexture::sample()), so uv *is* the texture coordinate. That also puts the
1299 // texture's first pixel row at v = 0, exactly where sample() reads it.
1300 init_data.add_vertex(Vec3f(lo.x(), lo.y(), 0.f), Vec2f(lo.x(), lo.y()));
1301 init_data.add_vertex(Vec3f(hi.x(), lo.y(), 0.f), Vec2f(hi.x(), lo.y()));
1302 init_data.add_vertex(Vec3f(hi.x(), hi.y(), 0.f), Vec2f(hi.x(), hi.y()));
1303 init_data.add_vertex(Vec3f(lo.x(), hi.y(), 0.f), Vec2f(lo.x(), hi.y()));
1304 init_data.add_triangle(0, 1, 2);
1305 init_data.add_triangle(0, 2, 3);
1306 m_background_glmodel.init_from(std::move(init_data));
1307}
GLModel m_background_glmodel
Definition UVEditorCanvas.hpp:310
int m_background_width
Definition UVEditorCanvas.hpp:302
bool m_background_quad_dirty
Definition UVEditorCanvas.hpp:304
int m_background_height
Definition UVEditorCanvas.hpp:302
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:65

References Slic3r::GUI::GLModel::Geometry::add_triangle(), Slic3r::GUI::GLModel::Geometry::add_vertex(), Slic3r::GUI::GLModel::Geometry::format, framed_bounds(), m_background_glmodel, m_background_height, m_background_quad_dirty, m_background_width, m_tile_enabled, Slic3r::GUI::GLModel::Geometry::P3T2, Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), and Slic3r::GUI::GLModel::Geometry::Triangles.

Referenced by render().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rebuild_background_texture()

void Slic3r::GUI::UVEditorCanvas::rebuild_background_texture ( )
private
1310{
1311 m_background_texture.reset();
1312 m_background_dirty = false;
1314 m_background_texture.load_from_raw_data(m_background_pixels, (unsigned int) m_background_width,
1315 (unsigned int) m_background_height, false);
1316 // The quad's extent doesn't depend on the pixels, but it does have to exist alongside them.
1318}
std::vector< unsigned char > m_background_pixels
Definition UVEditorCanvas.hpp:301
bool m_background_dirty
Definition UVEditorCanvas.hpp:303
GLTexture m_background_texture
Definition UVEditorCanvas.hpp:305

References m_background_dirty, m_background_height, m_background_pixels, m_background_quad_dirty, m_background_texture, and m_background_width.

Referenced by render().

Here is the caller graph for this function:

◆ rebuild_grid()

void Slic3r::GUI::UVEditorCanvas::rebuild_grid ( )
private
1186{
1187 // One line per UV unit (i.e. per texture tile), plus a subdivision when zoomed in far enough that
1188 // whole tiles would be too coarse to read.
1189 const float span = 2.f * m_zoom;
1190 float step = 1.f;
1191 while (step > 0.01f && span / step > 40.f)
1192 step *= 2.f;
1193 while (span / step < 4.f)
1194 step *= 0.5f;
1195
1196 // Cover exactly the currently-visible view rectangle (plus a one-step margin), recomputed on every
1197 // render. The old version only rebuilt when `step` changed, so panning at a fixed zoom left the grid
1198 // frozen at wherever it was last built - which is the "not in whole area / not always rendered while
1199 // moving" the user saw. The grid is only a few dozen lines, so rebuilding it per frame is cheap.
1200 float half_w, half_h;
1201 view_half_extents(half_w, half_h);
1202 const Vec2f lo = m_pan - Vec2f(half_w, half_h) - Vec2f(step, step);
1203 const Vec2f hi = m_pan + Vec2f(half_w, half_h) + Vec2f(step, step);
1204 m_grid_step = step;
1205
1206 const int first_x = int(std::floor(lo.x() / step)), last_x = int(std::ceil(hi.x() / step));
1207 const int first_y = int(std::floor(lo.y() / step)), last_y = int(std::ceil(hi.y() / step));
1208 if (last_x - first_x > 4000 || last_y - first_y > 4000)
1209 return; // degenerate zoom; not worth drawing a grid nobody can see
1210
1211 GLModel::Geometry grid;
1213 unsigned index = 0;
1214 const auto line = [&](const Vec2f &a, const Vec2f &b) {
1215 grid.add_vertex(Vec3f(a.x(), a.y(), 0.f));
1216 grid.add_vertex(Vec3f(b.x(), b.y(), 0.f));
1217 grid.add_line(index, index + 1);
1218 index += 2;
1219 };
1220 for (int i = first_x; i <= last_x; ++i)
1221 line(Vec2f(float(i) * step, lo.y()), Vec2f(float(i) * step, hi.y()));
1222 for (int i = first_y; i <= last_y; ++i)
1223 line(Vec2f(lo.x(), float(i) * step), Vec2f(hi.x(), float(i) * step));
1224
1225 m_grid_glmodel.reset();
1226 if (!grid.is_empty())
1227 m_grid_glmodel.init_from(std::move(grid));
1228}
void view_half_extents(float &half_w, float &half_h) const
Definition UVEditorCanvas.cpp:471
GLModel m_grid_glmodel
Definition UVEditorCanvas.hpp:292
float m_grid_step
Definition UVEditorCanvas.hpp:293
std::vector< ArithmeticOnly< T > > grid(const T &start, const T &stop, const T &stride)
A set of equidistant values starting from 'start' (inclusive), ending in the closest multiple of 'str...
Definition MTUtils.hpp:127
void step(Coord &crd, const Dir d, const long n=1)
Definition MarchingSquares.hpp:213

References Slic3r::grid(), Slic3r::GUI::GLModel::Geometry::Lines, m_grid_glmodel, m_grid_step, m_pan, m_zoom, Slic3r::GUI::GLModel::Geometry::P3, and view_half_extents().

Referenced by render().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ rebuild_island_models()

void Slic3r::GUI::UVEditorCanvas::rebuild_island_models ( )
private
1111{
1112 m_mesh_dirty = false;
1113 m_island_wireframe.clear();
1114 m_island_fill.clear();
1115
1116 const int islands = std::max(m_islands.island_count, 0);
1117 if (islands == 0 || m_islands.uvs.empty() || m_islands.indices.empty())
1118 return;
1119
1120 m_island_wireframe.resize(size_t(islands));
1121 m_island_fill.resize(size_t(islands));
1122
1123 const int vertex_count = int(m_islands.uvs.size());
1124
1125 // Charts have disjoint vertex sets (compute_patch_unwrap() duplicates seam vertices per chart),
1126 // so every vertex belongs to exactly one island and this partition is clean.
1127 std::unordered_map<uint64_t, bool> is_boundary;
1128 is_boundary.reserve(m_islands.boundary_edges.size() * 2);
1129 for (const auto &[a, b] : m_islands.boundary_edges)
1130 is_boundary[undirected_edge_key(a, b)] = true;
1131
1132 // Named, not size_t(islands) inline: the latter is a most-vexing-parse and declares a function
1133 // (a single identifier in the parens reads as a parameter name), which is what every
1134 // "subscript requires array or pointer type" error on these vectors was.
1135 const size_t n_islands = size_t(islands);
1136
1137 // Global vertex index -> index within its own island's buffers.
1138 std::vector<int> local(m_islands.uvs.size(), -1);
1139 std::vector<std::vector<Vec2f>> island_verts(n_islands);
1140 for (size_t i = 0; i < m_islands.uvs.size(); ++i) {
1141 const int island = m_islands.vertex_island[i];
1142 if (island < 0 || island >= islands)
1143 continue;
1144 local[i] = int(island_verts[size_t(island)].size());
1145 island_verts[size_t(island)].push_back(m_islands.uvs[i]);
1146 }
1147
1148 std::vector<GLModel::Geometry> wire(n_islands), fill(n_islands);
1149 for (int c = 0; c < islands; ++c) {
1152 for (GLModel::Geometry *g : { &wire[size_t(c)], &fill[size_t(c)] }) {
1153 g->reserve_vertices(island_verts[size_t(c)].size());
1154 for (const Vec2f &uv : island_verts[size_t(c)])
1155 g->add_vertex(Vec3f(uv.x(), uv.y(), 0.f));
1156 }
1157 }
1158
1159 for (const Vec3i32 &tri : m_islands.indices) {
1160 if (tri.minCoeff() < 0 || tri.maxCoeff() >= vertex_count)
1161 continue;
1162 const int island = m_islands.vertex_island[size_t(tri[0])];
1163 if (island < 0 || island >= islands)
1164 continue;
1165
1166 fill[size_t(island)].add_triangle(unsigned(local[size_t(tri[0])]), unsigned(local[size_t(tri[1])]),
1167 unsigned(local[size_t(tri[2])]));
1168 // Interior edges only: the island outline is drawn separately, brighter and on top, so drawing
1169 // it here as well would just dim it by blending against itself.
1170 for (int i = 0; i < 3; ++i) {
1171 const int a = tri[i], b = tri[(i + 1) % 3];
1172 if (!is_boundary.count(undirected_edge_key(a, b)))
1173 wire[size_t(island)].add_line(unsigned(local[size_t(a)]), unsigned(local[size_t(b)]));
1174 }
1175 }
1176
1177 for (int c = 0; c < islands; ++c) {
1178 if (!wire[size_t(c)].is_empty())
1179 m_island_wireframe[size_t(c)].init_from(std::move(wire[size_t(c)]));
1180 if (!fill[size_t(c)].is_empty())
1181 m_island_fill[size_t(c)].init_from(std::move(fill[size_t(c)]));
1182 }
1183}
std::vector< GLModel > m_island_wireframe
Definition UVEditorCanvas.hpp:285
std::vector< GLModel > m_island_fill
Definition UVEditorCanvas.hpp:286
@ local
Definition Time.hpp:13
uint64_t undirected_edge_key(int a, int b)
Definition TextureDisplacement.cpp:912

References Slic3r::GUI::GLModel::Geometry::Lines, m_island_fill, m_island_wireframe, m_islands, m_mesh_dirty, Slic3r::GUI::GLModel::Geometry::P3, and Slic3r::GUI::GLModel::Geometry::Triangles.

Referenced by render().

Here is the caller graph for this function:

◆ rebuild_rotation_dial()

void Slic3r::GUI::UVEditorCanvas::rebuild_rotation_dial ( )
private
1231{
1232 m_dial_glmodel.reset();
1234 if (!rotating || m_selected_island < 0)
1235 return;
1236
1237 const wxSize size = GetSize();
1238 const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight())));
1239 const float radius = 90.f * uv_per_px; // ~constant on-screen size regardless of zoom
1240 const Vec2f centre = island_centroid(m_selected_island);
1241
1242 GLModel::Geometry dial;
1244 unsigned index = 0;
1245 const auto line = [&](const Vec2f &a, const Vec2f &b) {
1246 dial.add_vertex(Vec3f(a.x(), a.y(), 0.f));
1247 dial.add_vertex(Vec3f(b.x(), b.y(), 0.f));
1248 dial.add_line(index, index + 1);
1249 index += 2;
1250 };
1251 const auto on_ring = [&](float deg, float r) {
1252 const float a = deg * float(M_PI) / 180.f;
1253 return centre + r * Vec2f(std::cos(a), std::sin(a));
1254 };
1255
1256 // The ring itself.
1257 constexpr int SEG = 72;
1258 for (int i = 0; i < SEG; ++i)
1259 line(on_ring(float(i) * 360.f / SEG, radius), on_ring(float(i + 1) * 360.f / SEG, radius));
1260 // A tick every 15 degrees (the snap marks), longer on the cardinals.
1261 for (int d = 0; d < 360; d += 15) {
1262 const bool cardinal = (d % 90) == 0;
1263 line(on_ring(float(d), radius * (cardinal ? 0.80f : 0.90f)), on_ring(float(d), radius * (cardinal ? 1.08f : 1.0f)));
1264 }
1265 // The needle at the island's current absolute angle.
1266 line(centre, on_ring(m_rot_display_deg, radius * 1.12f));
1267
1268 if (!dial.is_empty())
1269 m_dial_glmodel.init_from(std::move(dial));
1270}
GLModel m_dial_glmodel
Definition UVEditorCanvas.hpp:396

References Slic3r::GUI::GLModel::Geometry::add_line(), Slic3r::GUI::GLModel::Geometry::add_vertex(), Slic3r::GUI::GLModel::Geometry::format, Slic3r::GUI::GLModel::Geometry::is_empty(), island_centroid(), Slic3r::GUI::GLModel::Geometry::Lines, m_dial_glmodel, m_gesture, M_PI, m_rot_display_deg, m_selected_island, m_zoom, Slic3r::GUI::GLModel::Geometry::P3, RotateIsland, and RotateIslandModal.

Referenced by render().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ render()

void Slic3r::GUI::UVEditorCanvas::render ( )
private
1358{
1359 if (m_context == nullptr || !IsShownOnScreen())
1360 return;
1361 // Painted from inside a 3D canvas frame - a modal dialog the gizmo opened from its ImGui panel runs
1362 // this paint, as does any other synchronous event raised there. Making this surface current now
1363 // would take the shared context away from that canvas mid-frame: its remaining draws would land
1364 // here and, on Mesa/X11, its SwapBuffers() would be a fatal BadMatch. Skip, but ask for another
1365 // paint so the skipped one is not simply lost. The pane stays stale for as long as a nested modal
1366 // loop keeps a frame on the stack, which is behind the dialog anyway.
1368 Refresh();
1369 return;
1370 }
1371
1372 // wxGLCanvas::SetCurrent() genuinely fails (returns false) on a canvas that isn't shown on
1373 // screen, and can also fail on a pixel-format mismatch with the context - and every GL call
1374 // made afterwards would then run against whichever context *is* current, which here is the main
1375 // 3D canvas mid-frame. Bail instead of corrupting it.
1376 if (!SetCurrent(*m_context))
1377 return;
1378
1379 // The context is shared with the 3D view, which leaves its own state behind: GLCanvas3D turns face culling on at
1380 // init and many of its passes turn it back on, and the pane's projection flips Y, so every triangle drawn here
1381 // faces away from it. With culling left on, the texture and the islands simply vanish - which is why they showed
1382 // the first time and were gone, fully or partly, after the 3D view had drawn. A scissor rectangle left on would
1383 // clip the clear and everything else the same way. Set what this pass relies on; put the 3D view's back after.
1384 const GLboolean prev_cull = ::glIsEnabled(GL_CULL_FACE);
1385 const GLboolean prev_scissor = ::glIsEnabled(GL_SCISSOR_TEST);
1386 const GLboolean prev_stencil = ::glIsEnabled(GL_STENCIL_TEST);
1392
1393 if (m_gl_reset_pending) {
1394 m_gl_reset_pending = false;
1395 m_mesh_dirty = true;
1396 m_background_dirty = true; // re-uploads m_background_pixels, or drops the texture if there are none
1397 m_tile_outline_glmodel.reset();
1399 m_dim_quad_glmodel.reset();
1400 m_stroke_glmodel.reset();
1401 m_stencil_bits = -1;
1402 }
1403 if (m_mesh_dirty)
1409 if (m_needs_fit)
1411 rebuild_grid();
1413
1414 const wxSize size = GetSize(); // logical points; the on-screen handle sizes below use it
1415 const wxSize viewport = gl_drawable_size(this, size);
1416 glsafe(::glViewport(0, 0, viewport.GetWidth(), viewport.GetHeight()));
1417 // Line widths below are authored in logical points (they are chosen against the same scale the
1418 // hit-test thresholds use), so they take the same logical -> device conversion as the viewport.
1419 const float px_scale = float(viewport.GetWidth()) / float(std::max(1, size.GetWidth()));
1420 const auto line_width = [px_scale](float w) { set_line_width(w * px_scale); };
1423 glsafe(::glStencilMask(0xFF));
1428
1429 // Orthographic, centered on m_pan. Y is negated so v runs down the screen, which is what puts
1430 // the texture's first pixel row (v = 0, see rebuild_background_quad()) at the top rather than
1431 // upside down.
1432 float half_w, half_h;
1433 view_half_extents(half_w, half_h);
1434
1435 Transform3d projection_matrix = Transform3d::Identity();
1436 projection_matrix(0, 0) = 1.0 / std::max(double(half_w), 1e-6);
1437 projection_matrix(1, 1) = -1.0 / std::max(double(half_h), 1e-6);
1438 Transform3d view_matrix = Transform3d::Identity();
1439 view_matrix.translate(Vec3d(-double(m_pan.x()), -double(m_pan.y()), 0.0));
1440
1441 const auto draw_raw = [&](GLModel &model, const ColorRGBA &color, const Transform3d &view_model, const Transform3d &projection) {
1442 if (!model.is_initialized())
1443 return;
1444 GLShaderProgram *shader = wxGetApp().get_shader("flat");
1445 if (shader == nullptr)
1446 return;
1447 shader->start_using();
1448 shader->set_uniform("view_model_matrix", view_model);
1449 shader->set_uniform("projection_matrix", projection);
1450 model.set_color(color);
1451 model.render();
1452 shader->stop_using();
1453 };
1454 const auto draw = [&](GLModel &model, const ColorRGBA &color, const Transform3d &model_matrix) {
1455 draw_raw(model, color, view_matrix * model_matrix, projection_matrix);
1456 };
1457 const Transform3d identity = Transform3d::Identity();
1458
1459 if (m_background_glmodel.is_initialized()) {
1460 if (GLShaderProgram *shader = wxGetApp().get_shader("flat_texture")) {
1461 shader->start_using();
1462 shader->set_uniform("view_model_matrix", view_matrix);
1463 shader->set_uniform("projection_matrix", projection_matrix);
1466 // Repeat exactly the way DecodedHeightTexture::sample() does, so the backdrop under an
1467 // island really is the texels that island will sample. With tiling off, sample() returns
1468 // 0 outside the first tile - a black border, not a smeared edge, which is what
1469 // CLAMP_TO_BORDER reproduces (GLTexture's own default of GL_REPEAT would not).
1473 if (!m_tile_enabled) {
1474 constexpr GLfloat border[4] = { 0.f, 0.f, 0.f, 1.f };
1476 }
1477 m_background_glmodel.render();
1479 shader->stop_using();
1480 }
1481 }
1482
1483 const auto island_matrix = [this, &identity](int c) {
1484 return (size_t(c) < m_transforms.size()) ? to_transform3d(m_transforms[size_t(c)]) : identity;
1485 };
1486 const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight())));
1487
1488 // Strokes are built as quads `width_px` wide on screen, rebuilt every paint at the current zoom. Square caps
1489 // cover the joints of an outline; the colours passed here are opaque, so the overlapping caps don't show.
1490 using Segments = std::vector<std::pair<Vec2f, Vec2f>>;
1491 const auto draw_segments = [&](const Segments &segments, float width_px, const ColorRGBA &color) {
1492 if (segments.empty())
1493 return;
1494 const float half = 0.5f * width_px * uv_per_px;
1495 GLModel::Geometry quads;
1497 quads.reserve_vertices(segments.size() * 4);
1498 quads.reserve_indices(segments.size() * 6);
1499 unsigned base = 0;
1500 for (const auto &[a, b] : segments) {
1501 const Vec2f d = b - a;
1502 const float len = d.norm();
1503 if (len <= 1e-12f)
1504 continue;
1505 const Vec2f t = d * (half / len);
1506 const Vec2f n(-t.y(), t.x());
1507 for (const Vec2f &p : { Vec2f(a - t + n), Vec2f(a - t - n), Vec2f(b + t - n), Vec2f(b + t + n) })
1508 quads.add_vertex(Vec3f(p.x(), p.y(), 0.f));
1509 quads.add_triangle(base, base + 1, base + 2);
1510 quads.add_triangle(base, base + 2, base + 3);
1511 base += 4;
1512 }
1513 m_stroke_glmodel.reset();
1514 if (quads.is_empty())
1515 return;
1516 m_stroke_glmodel.init_from(std::move(quads));
1518 };
1519 const auto draw_stroke = [&](const Segments &segments, float width_px, const ColorRGBA &color, float halo_px) {
1520 draw_segments(segments, width_px + 2.f * halo_px, UV_COLOR_HALO);
1521 draw_segments(segments, width_px, color);
1522 };
1523 const auto edge_segments = [this](const std::vector<std::pair<int, int>> &edges) {
1524 Segments out;
1525 out.reserve(edges.size());
1526 for (const auto &[a, b] : edges)
1527 if (a >= 0 && b >= 0 && size_t(a) < m_islands.uvs.size() && size_t(b) < m_islands.uvs.size())
1528 out.emplace_back(island_uv(size_t(a)), island_uv(size_t(b)));
1529 return out;
1530 };
1531
1532 // Dim the texture everywhere but inside the islands: the fills go into the stencil only, then one
1533 // full-viewport quad washes the rest towards the background. Skipped without a stencil buffer, where the
1534 // quad would cover the islands too.
1535 if (m_stencil_bits < 0) {
1536 GLint bits = 0;
1537 ::glGetError(); // drop anything pending, so the check below sees only this query
1538 ::glGetIntegerv(GL_STENCIL_BITS, &bits); // compatibility profiles
1539 if (::glGetError() != GL_NO_ERROR) {
1540 bits = 0;
1542 ::glGetError();
1543 }
1544 m_stencil_bits = std::max(0, int(bits));
1545 }
1546 if (has_islands() && !m_island_fill.empty() && m_stencil_bits > 0) {
1547 if (!m_dim_quad_glmodel.is_initialized()) {
1548 GLModel::Geometry q;
1550 q.add_vertex(Vec3f(-1.f, -1.f, 0.f));
1551 q.add_vertex(Vec3f(1.f, -1.f, 0.f));
1552 q.add_vertex(Vec3f(1.f, 1.f, 0.f));
1553 q.add_vertex(Vec3f(-1.f, 1.f, 0.f));
1554 q.add_triangle(0, 1, 2);
1555 q.add_triangle(0, 2, 3);
1556 m_dim_quad_glmodel.init_from(std::move(q));
1557 }
1559 glsafe(::glStencilFunc(GL_ALWAYS, 1, 0xFF));
1562 for (int c = 0; c < int(m_island_fill.size()); ++c)
1563 draw(m_island_fill[size_t(c)], ColorRGBA::WHITE(), island_matrix(c));
1565 glsafe(::glStencilFunc(GL_EQUAL, 0, 0xFF));
1567 draw_raw(m_dim_quad_glmodel, UV_COLOR_OUTSIDE, identity, identity);
1569 }
1570
1571 line_width(1.f);
1572 draw(m_grid_glmodel, UV_COLOR_GRID, identity);
1573
1574 // The texture's first tile. Always drawn, even with nothing painted, so the pane always has a
1575 // fixed landmark: the unwrap is packed in mm and divided by the tile size, so it is routinely
1576 // many tiles away from here, and without this there is no way to tell "the islands are somewhere
1577 // else" apart from "there are no islands".
1578 draw_stroke({ { Vec2f(0.f, 0.f), Vec2f(1.f, 0.f) }, { Vec2f(1.f, 0.f), Vec2f(1.f, 1.f) },
1579 { Vec2f(1.f, 1.f), Vec2f(0.f, 1.f) }, { Vec2f(0.f, 1.f), Vec2f(0.f, 0.f) } },
1580 1.5f, UV_COLOR_TILE_OUTLINE, 1.f);
1581
1582 // Island fills. Unselected islands get none, so the texture that will be baked shows unaltered; the one a
1583 // click would grab gets a light wash, the selection a teal one. A distortion heatmap (set_island_fill_colors)
1584 // replaces the default for unselected islands and is left alone by the hover wash.
1585 for (int c = 0; c < int(m_island_fill.size()); ++c) {
1586 ColorRGBA fill = size_t(c) < m_island_fill_colors.size() ? m_island_fill_colors[size_t(c)] : UV_COLOR_FILL;
1587 if (is_selected(c))
1588 fill = UV_COLOR_SEL_FILL;
1589 else if (c == m_hover_island && m_island_fill_colors.empty())
1590 fill = UV_COLOR_HOVER_FILL;
1591 if (fill.a() > 0.f)
1592 draw(m_island_fill[size_t(c)], fill, island_matrix(c));
1593 }
1594
1595 // Interior edges stay GL lines (a patch can have a million of them), each with a dark copy one pixel down
1596 // and right, so the light wire still reads on a light texture.
1597 Transform3d shadow_projection = projection_matrix;
1598 shadow_projection.pretranslate(Vec3d(2.0 / std::max(1, size.GetWidth()), -2.0 / std::max(1, size.GetHeight()), 0.0));
1599 line_width(1.f);
1600 for (int c = 0; c < int(m_island_wireframe.size()); ++c) {
1601 draw_raw(m_island_wireframe[size_t(c)], UV_COLOR_WIRE_SHADOW, view_matrix * island_matrix(c), shadow_projection);
1602 draw(m_island_wireframe[size_t(c)], is_selected(c) ? UV_COLOR_SEL_WIRE : UV_COLOR_WIRE, island_matrix(c));
1603 }
1604
1605 // The island outlines - i.e. exactly the edges the seam angle cut the patch along - because "where does one
1606 // island end and the next begin" is the single thing this pane exists to answer. Plain first, then the hovered
1607 // one, then the selection, so a highlighted outline is never overdrawn by its neighbour's.
1608 {
1609 std::vector<std::pair<int, int>> plain, hovered, selected;
1610 for (int c = 0; c < int(m_island_boundary_edges.size()); ++c) {
1611 auto &dst = is_selected(c) ? selected : (c == m_hover_island ? hovered : plain);
1612 dst.insert(dst.end(), m_island_boundary_edges[size_t(c)].begin(), m_island_boundary_edges[size_t(c)].end());
1613 }
1614 draw_stroke(edge_segments(plain), 1.5f, UV_COLOR_BOUNDARY, 1.25f);
1615 draw_stroke(edge_segments(hovered), 2.5f, UV_COLOR_HOVER, 1.5f);
1616 draw_stroke(edge_segments(selected), 3.f, UV_COLOR_SEL_BOUNDARY, 1.75f);
1617 }
1618
1619 // The rotation protractor, on top of everything while a rotation gesture is live (#11).
1620 if (m_dial_glmodel.is_initialized()) {
1621 line_width(2.f);
1622 draw(m_dial_glmodel, UV_COLOR_DIAL, identity);
1623 line_width(1.f);
1624 }
1625
1626 // Vertex/Edge mode: the element under the cursor in the hover colour, and the selection as teal - a selected
1627 // edge drawn over its whole length, a vertex as a square handle, both on a dark halo.
1628 if (!m_vertex_marker_glmodel.is_initialized()) {
1629 GLModel::Geometry q;
1631 q.reserve_vertices(4);
1632 q.reserve_indices(6);
1633 q.add_vertex(Vec3f(-0.5f, -0.5f, 0.f));
1634 q.add_vertex(Vec3f(0.5f, -0.5f, 0.f));
1635 q.add_vertex(Vec3f(0.5f, 0.5f, 0.f));
1636 q.add_vertex(Vec3f(-0.5f, 0.5f, 0.f));
1637 q.add_triangle(0, 1, 2);
1638 q.add_triangle(0, 2, 3);
1639 m_vertex_marker_glmodel.init_from(std::move(q));
1640 }
1641 const auto draw_marker = [&](int v, float px, const ColorRGBA &color) {
1642 if (v < 0 || size_t(v) >= m_islands.uvs.size())
1643 return;
1644 const Vec2f p = island_uv(size_t(v));
1645 Transform3d m = Transform3d::Identity();
1646 m.translate(Vec3d(double(p.x()), double(p.y()), 0.0));
1647 m.scale(double(px * uv_per_px));
1648 draw(m_vertex_marker_glmodel, color, m);
1649 };
1650 const auto draw_handle = [&](int v, const ColorRGBA &color, float px) {
1651 draw_marker(v, px + 4.f, UV_COLOR_HALO);
1652 draw_marker(v, px, color);
1653 };
1656 draw_handle(m_hover_vertex, UV_COLOR_HOVER, 7.f);
1657 // Every element of the multi-selection, falling back to the primary while the set is still empty.
1658 if (m_sel_vertices.empty())
1659 draw_handle(m_active_vertex, UV_COLOR_SEL_BOUNDARY, 8.f);
1660 else
1661 for (const int v : m_sel_vertices)
1662 draw_handle(v, UV_COLOR_SEL_BOUNDARY, 8.f);
1663 } else if (m_select_mode == SelectMode::Edge) {
1664 if (m_hover_edge.first >= 0 && !is_edge_selected(m_hover_edge))
1665 draw_stroke(edge_segments({ m_hover_edge }), 3.f, UV_COLOR_HOVER, 2.f);
1666 std::vector<std::pair<int, int>> edges = m_sel_edges;
1667 if (edges.empty() && m_active_edge.first >= 0)
1668 edges.push_back(m_active_edge);
1669 draw_stroke(edge_segments(edges), 4.f, UV_COLOR_SEL_BOUNDARY, 2.f);
1670 for (const auto &[a, b] : edges) {
1671 draw_handle(a, UV_COLOR_SEL_BOUNDARY, 6.f);
1672 draw_handle(b, UV_COLOR_SEL_BOUNDARY, 6.f);
1673 }
1674 }
1675
1676 // Add/remove hint next to the cursor in Vertex/Edge mode: a green '+' when a click will add to the
1677 // selection (plain or Shift), a red '-' when Ctrl is held and a click will remove one - the UV-side
1678 // twin of the 3D paint cursor's own sign.
1680 const Vec2f centre = screen_to_uv(m_cursor_px) + Vec2f(14.f, -14.f) * uv_per_px; // up-right of the pointer
1681 const float r = 6.f * uv_per_px;
1682 const bool removing = wxGetKeyState(WXK_CONTROL);
1683 Segments sign{ { centre - Vec2f(r, 0.f), centre + Vec2f(r, 0.f) } }; // the '-' bar, shared by both signs
1684 if (!removing)
1685 sign.emplace_back(centre - Vec2f(0.f, r), centre + Vec2f(0.f, r)); // the extra stroke that makes it a '+'
1686 draw_stroke(sign, 2.5f, removing ? ColorRGBA(1.f, 0.36f, 0.30f, 1.f) : ColorRGBA(0.40f, 0.92f, 0.50f, 1.f), 1.f);
1687 }
1688
1689 // The GL context is shared with the 3D view; leave the bits we touched as we found them.
1692 if (prev_cull)
1694 if (prev_scissor)
1696 if (prev_stencil)
1698
1699 SwapBuffers();
1700
1701 // Cheap, and render() runs on every gesture change (each Refresh), so the status line stays
1702 // current without threading update_status() through every mouse/key transition. The panel
1703 // guards against relayout when the text is unchanged.
1704 update_status();
1705}
#define glsafe(cmd)
Definition OpenGLUtils.hpp:25
float b() const
Definition Color.hpp:107
float r() const
Definition Color.hpp:105
float g() const
Definition Color.hpp:106
static const ColorRGBA WHITE()
Definition Color.hpp:142
GLModel m_dim_quad_glmodel
Definition UVEditorCanvas.hpp:288
void rebuild_background_quad()
Definition UVEditorCanvas.cpp:1272
void fit_view_to_content()
Definition UVEditorCanvas.cpp:451
std::vector< ColorRGBA > m_island_fill_colors
Definition UVEditorCanvas.hpp:270
void update_status()
Definition UVEditorCanvas.cpp:361
void rebuild_background_texture()
Definition UVEditorCanvas.cpp:1309
int m_stencil_bits
Definition UVEditorCanvas.hpp:289
void rebuild_rotation_dial()
Definition UVEditorCanvas.cpp:1230
std::vector< std::vector< std::pair< int, int > > > m_island_boundary_edges
Definition UVEditorCanvas.hpp:276
bool m_tile_mirrored
Definition UVEditorCanvas.hpp:298
void rebuild_grid()
Definition UVEditorCanvas.cpp:1185
GLModel m_vertex_marker_glmodel
Definition UVEditorCanvas.hpp:361
void rebuild_island_models()
Definition UVEditorCanvas.cpp:1110
GLModel m_tile_outline_glmodel
Definition UVEditorCanvas.hpp:291
GLModel m_stroke_glmodel
Definition UVEditorCanvas.hpp:287
bool has_islands() const
Definition UVEditorCanvas.hpp:186
#define GL_MIRRORED_REPEAT
Definition gl.h:1187
#define GL_TRUE
Definition gl.h:1892
#define GL_ALWAYS
Definition gl.h:230
#define glGetError
Definition gl.h:3849
#define GL_STENCIL_BITS
Definition gl.h:1645
#define glClearColor
Definition gl.h:3373
#define GL_TEXTURE0
Definition gl.h:1705
#define GL_TEXTURE_BORDER_COLOR
Definition gl.h:1762
#define GL_TEXTURE_WRAP_S
Definition gl.h:1854
#define GL_FRONT_AND_BACK
Definition gl.h:761
#define glEnable
Definition gl.h:3667
#define GL_DEPTH_TEST
Definition gl.h:574
#define GL_STENCIL_TEST
Definition gl.h:1664
#define GL_SRC_ALPHA
Definition gl.h:1621
#define glTexParameteri
Definition gl.h:4911
int GLint
Definition gl.h:2062
#define GL_STENCIL
Definition gl.h:1635
#define glStencilOp
Definition gl.h:4783
#define glGetIntegerv
Definition gl.h:3873
#define glTexParameterfv
Definition gl.h:4909
#define glColorMask
Definition gl.h:3471
#define GL_DRAW_FRAMEBUFFER
Definition gl.h:617
khronos_float_t GLfloat
Definition gl.h:2066
#define GL_NO_ERROR
Definition gl.h:1217
#define glStencilFunc
Definition gl.h:4775
#define glGetFramebufferAttachmentParameteriv
Definition gl.h:3859
#define GL_CULL_FACE
Definition gl.h:500
#define GL_COLOR_BUFFER_BIT
Definition gl.h:408
#define GL_FILL
Definition gl.h:654
#define glBindTexture
Definition gl.h:3299
unsigned char GLboolean
Definition gl.h:2055
#define GL_BLEND
Definition gl.h:275
#define GL_ONE_MINUS_SRC_ALPHA
Definition gl.h:1239
#define GL_REPEAT
Definition gl.h:1453
#define GL_CLAMP_TO_BORDER
Definition gl.h:321
#define glClearStencil
Definition gl.h:3393
#define glViewport
Definition gl.h:5333
#define GL_FALSE
Definition gl.h:648
#define GL_SCISSOR_TEST
Definition gl.h:1550
#define GL_STENCIL_BUFFER_BIT
Definition gl.h:1646
#define glIsEnabled
Definition gl.h:4165
#define GL_TEXTURE_WRAP_T
Definition gl.h:1855
#define GL_EQUAL
Definition gl.h:640
#define glStencilMask
Definition gl.h:4779
#define glPolygonMode
Definition gl.h:4469
#define GL_TEXTURE_2D
Definition gl.h:1739
#define glDisable
Definition gl.h:3607
#define GL_REPLACE
Definition gl.h:1454
#define glActiveTexture
Definition gl.h:3245
#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE
Definition gl.h:721
#define GL_KEEP
Definition gl.h:909
#define glBlendFunc
Definition gl.h:3325
#define glClear
Definition gl.h:3357
Transform3d to_transform3d(const UVEditorCanvas::IslandTransform &m)
Definition UVEditorCanvas.cpp:173
const ColorRGBA UV_COLOR_TILE_OUTLINE
Definition UVEditorCanvas.cpp:64
wxSize gl_drawable_size(const wxWindow *win, const wxSize &logical_size)
Definition UVEditorCanvas.cpp:85
const ColorRGBA UV_COLOR_FILL
Definition UVEditorCanvas.cpp:71
const ColorRGBA UV_COLOR_HOVER
Definition UVEditorCanvas.cpp:68
const ColorRGBA UV_COLOR_HOVER_FILL
Definition UVEditorCanvas.cpp:69
const ColorRGBA UV_COLOR_SEL_FILL
Definition UVEditorCanvas.cpp:72
void set_line_width(float width)
Definition UVEditorCanvas.cpp:136
const ColorRGBA UV_COLOR_BG
Definition UVEditorCanvas.cpp:56
void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_col, const ImVec4 &border_col)
Draw imgui image with icon.
Definition IconManager.cpp:373
int64_t px(const Point &pt)
Definition ClipperUtils.cpp:207
Transform< N, T > identity()
Definition Point.hpp:106
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68
Eigen::Transform< double, 3, Eigen::Affine, Eigen::DontAlign > Transform3d
Definition Point.hpp:96
@ hovered
Definition GLGizmoEmboss.cpp:223

References Slic3r::ColorRGBA::a(), Slic3r::GUI::GLModel::Geometry::add_triangle(), Slic3r::GUI::GLModel::Geometry::add_vertex(), Slic3r::ColorRGBA::b(), Slic3r::GUI::draw(), Edge, fit_view_to_content(), Slic3r::GUI::GLModel::Geometry::format, Slic3r::ColorRGBA::g(), GL_ALWAYS, GL_BLEND, GL_CLAMP_TO_BORDER, GL_COLOR_BUFFER_BIT, GL_CULL_FACE, GL_DEPTH_TEST, GL_DRAW_FRAMEBUFFER, GL_EQUAL, GL_FALSE, GL_FILL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, GL_FRONT_AND_BACK, GL_KEEP, GL_MIRRORED_REPEAT, GL_NO_ERROR, GL_ONE_MINUS_SRC_ALPHA, GL_REPEAT, GL_REPLACE, GL_SCISSOR_TEST, GL_SRC_ALPHA, GL_STENCIL, GL_STENCIL_BITS, GL_STENCIL_BUFFER_BIT, GL_STENCIL_TEST, GL_TEXTURE0, GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_TRUE, glActiveTexture, glBindTexture, glBlendFunc, glClear, glClearColor, glClearStencil, glColorMask, glDisable, glEnable, glGetError, glGetFramebufferAttachmentParameteriv, glGetIntegerv, glIsEnabled, glPolygonMode, glsafe, glStencilFunc, glStencilMask, glStencilOp, glTexParameterfv, glTexParameteri, glViewport, has_islands(), Slic3r::identity(), is_edge_selected(), Slic3r::GUI::GLModel::Geometry::is_empty(), Slic3r::GUI::GLCanvas3D::is_frame_in_progress(), Slic3r::GUI::GLModel::is_initialized(), is_selected(), is_vertex_selected(), Island, island_uv(), m_active_edge, m_active_vertex, m_background_dirty, m_background_glmodel, m_background_quad_dirty, m_background_texture, m_context, m_cursor_inside, m_cursor_px, m_dial_glmodel, m_dim_quad_glmodel, m_gl_reset_pending, m_grid_glmodel, m_hover_edge, m_hover_island, m_hover_vertex, m_island_boundary_edges, m_island_fill, m_island_fill_colors, m_island_wireframe, m_islands, m_mesh_dirty, m_needs_fit, m_pan, m_sel_edges, m_sel_vertices, m_select_mode, m_stencil_bits, m_stroke_glmodel, m_tile_enabled, m_tile_mirrored, m_tile_outline_glmodel, m_transforms, m_vertex_marker_glmodel, m_zoom, Slic3r::GUI::GLModel::Geometry::P3, Slic3r::ColorRGBA::r(), rebuild_background_quad(), rebuild_background_texture(), rebuild_grid(), rebuild_island_models(), rebuild_rotation_dial(), Slic3r::GUI::GLModel::render(), Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), screen_to_uv(), Slic3r::GUI::GLModel::set_color(), Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), Slic3r::GUI::GLModel::Geometry::Triangles, update_status(), Vertex, view_half_extents(), and Slic3r::ColorRGBA::WHITE().

Referenced by on_paint().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset_view()

void Slic3r::GUI::UVEditorCanvas::reset_view ( )
331{
332 m_needs_fit = true;
333 Refresh();
334}

References m_needs_fit.

Referenced by on_key(), and run_command().

Here is the caller graph for this function:

◆ run_command()

void Slic3r::GUI::UVEditorCanvas::run_command ( Command cmd,
float value = 0.f )
337{
338 switch (cmd) {
339 case Command::FrameAll: reset_view(); return;
342 // Switched here straight away so the strip and the canvas agree at once; the gizmo is still told,
343 // because it is what keeps the mode when the canvas is next refreshed from the layer.
344 set_select_mode(static_cast<SelectMode>(std::clamp(int(value), 0, 2)));
345 break;
346 default: break;
347 }
348 // Everything else needs the layer data the canvas doesn't hold; hand it to the gizmo.
349 if (m_on_command)
350 m_on_command(cmd, value);
351}
SelectMode
Definition UVEditorCanvas.hpp:171
@ FrameAll
Definition UVEditorCanvas.hpp:114
@ SetSelectMode
Definition UVEditorCanvas.hpp:115
@ ToggleSnap
Definition UVEditorCanvas.hpp:114
void set_select_mode(SelectMode mode)
Definition UVEditorCanvas.cpp:556
CommandFn m_on_command
Definition UVEditorCanvas.hpp:403
bool m_snap_enabled
Definition UVEditorCanvas.hpp:299

References FrameAll, m_on_command, m_snap_enabled, reset_view(), set_select_mode(), SetSelectMode, ToggleSnap, and update_status().

Referenced by on_mouse().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ screen_to_uv()

Vec2f Slic3r::GUI::UVEditorCanvas::screen_to_uv ( const wxPoint & px) const
private
486{
487 const wxSize size = GetSize();
488 const float w = float(std::max(1, size.GetWidth()));
489 const float h = float(std::max(1, size.GetHeight()));
490
491 float half_w, half_h;
492 view_half_extents(half_w, half_h);
493
494 // Inverse of the projection in render(): x maps straight through, y is negated there so that v
495 // runs down the screen - which means screen-down and v-increasing agree, and this is a plain
496 // scale on both axes.
497 return Vec2f(m_pan.x() + (2.f * float(px.x) / w - 1.f) * half_w,
498 m_pan.y() + (2.f * float(px.y) / h - 1.f) * half_h);
499}

References m_pan, and view_half_extents().

Referenced by on_key(), on_mouse(), render(), and update_hover().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ select_mode()

SelectMode Slic3r::GUI::UVEditorCanvas::select_mode ( ) const
inline
173{ return m_select_mode; }

References m_select_mode.

◆ selected_edge_endpoints()

std::vector< int > Slic3r::GUI::UVEditorCanvas::selected_edge_endpoints ( ) const
private
702{
703 std::vector<int> verts;
704 for (const auto &[a, b] : m_sel_edges)
705 for (const int v : { a, b })
706 if (v >= 0 && std::find(verts.begin(), verts.end(), v) == verts.end())
707 verts.push_back(v);
708 return verts;
709}

References m_sel_edges.

Referenced by end_gesture(), and on_mouse().

Here is the caller graph for this function:

◆ selected_island()

int Slic3r::GUI::UVEditorCanvas::selected_island ( ) const
inline
184{ return m_selected_island; }

References m_selected_island.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::run_uv_command().

Here is the caller graph for this function:

◆ selected_islands()

const std::vector< int > & Slic3r::GUI::UVEditorCanvas::selected_islands ( ) const
inline
190{ return m_selection; }

References m_selection.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::island_highlight_selection().

Here is the caller graph for this function:

◆ set_background_texture()

void Slic3r::GUI::UVEditorCanvas::set_background_texture ( const std::vector< unsigned char > & grayscale_pixels,
int width,
int height )
310{
311 if (width <= 0 || height <= 0 || grayscale_pixels.size() != size_t(width) * size_t(height)) {
313 m_background_pixels.clear();
314 } else {
317 m_background_pixels.assign(size_t(width) * size_t(height) * 4, 255);
318 for (size_t i = 0; i < grayscale_pixels.size(); ++i) {
319 const unsigned char g = grayscale_pixels[i];
320 m_background_pixels[i * 4 + 0] = g;
321 m_background_pixels[i * 4 + 1] = g;
322 m_background_pixels[i * 4 + 2] = g;
323 m_background_pixels[i * 4 + 3] = 255;
324 }
325 }
326 m_background_dirty = true;
327 Refresh();
328}
coord_t width(const BoundingBox &box)
Definition Arrange.cpp:1184
coord_t height(const BoundingBox &box)
Definition Arrange.cpp:1185

References m_background_dirty, m_background_height, m_background_pixels, and m_background_width.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::update_uv_editor().

Here is the caller graph for this function:

◆ set_command_callback()

void Slic3r::GUI::UVEditorCanvas::set_command_callback ( CommandFn fn)
inline
120{ m_on_command = std::move(fn); }

References m_on_command.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::update_uv_editor().

Here is the caller graph for this function:

◆ set_island_edit_callback()

void Slic3r::GUI::UVEditorCanvas::set_island_edit_callback ( IslandEditFn fn)
inline
165{ m_on_island_edit = std::move(fn); }

References m_on_island_edit.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::update_uv_editor().

Here is the caller graph for this function:

◆ set_island_fill_colors()

void Slic3r::GUI::UVEditorCanvas::set_island_fill_colors ( std::vector< ColorRGBA > colors)
295{
296 m_island_fill_colors = std::move(colors);
297 Refresh();
298}

References m_island_fill_colors.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::update_uv_editor().

Here is the caller graph for this function:

◆ set_island_transforms()

void Slic3r::GUI::UVEditorCanvas::set_island_transforms ( std::vector< IslandTransform > transforms)
288{
289 m_transforms = std::move(transforms);
290 m_background_quad_dirty = true; // content_bounds() depends on where the islands ended up
291 Refresh();
292}

References m_background_quad_dirty, and m_transforms.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::update_uv_editor().

Here is the caller graph for this function:

◆ set_islands()

void Slic3r::GUI::UVEditorCanvas::set_islands ( Islands islands)
217{
218 // Only frame the view when a patch first appears, not on every stroke that extends one - the
219 // latter would keep yanking the view out from under a user who has panned or zoomed.
220 m_needs_fit |= m_islands.indices.empty();
221 // Re-uploads that keep the same island/vertex count are just a refresh (e.g. a committed vertex edit
222 // re-applied), not a re-segmentation, so the selection and the picked sub-element stay valid and are
223 // worth keeping. A change in either count means the charts were renumbered and both are meaningless.
224 const bool same_structure = m_islands.island_count == islands.island_count &&
225 m_islands.uvs.size() == islands.uvs.size();
226 m_islands = std::move(islands);
227 if (m_selected_island >= m_islands.island_count)
229 if (!same_structure) {
230 // A fresh unwrap renumbers charts, so any previous multi-selection is meaningless: drop it, then
231 // keep the primary (if still valid) as a single-island selection so the highlight is consistent.
232 m_selection.clear();
233 if (m_selected_island >= 0)
235 // Vertex/edge picks index into the old unwrap; drop them too.
236 m_active_vertex = -1;
237 m_active_edge = { -1, -1 };
238 m_sel_vertices.clear();
239 m_sel_edges.clear();
241 m_hover_edge = { -1, -1 };
242 }
243
244 // Boundary vertices and edges, bucketed per island: the vertices for snapping, the edges for the outline.
245 const size_t n_islands = size_t(std::max(m_islands.island_count, 0));
246 m_island_boundary_verts.assign(n_islands, {});
247 m_island_boundary_edges.assign(n_islands, {});
248 std::vector<bool> seen(m_islands.uvs.size(), false);
249 for (const auto &[a, b] : m_islands.boundary_edges) {
250 if (a < 0 || b < 0 || size_t(a) >= m_islands.uvs.size() || size_t(b) >= m_islands.uvs.size())
251 continue;
252 const int island = m_islands.vertex_island[size_t(a)];
253 if (island < 0 || size_t(island) >= n_islands)
254 continue;
255 m_island_boundary_edges[size_t(island)].emplace_back(a, b);
256 for (const int v : { a, b })
257 if (!seen[size_t(v)]) {
258 seen[size_t(v)] = true;
259 m_island_boundary_verts[size_t(island)].push_back(v);
260 }
261 }
262
263 // Triangles and raw bounds per island, for picking.
264 m_island_tris.assign(n_islands, {});
265 m_island_raw_bounds.assign(n_islands, { Vec2f::Constant(std::numeric_limits<float>::max()),
266 Vec2f::Constant(std::numeric_limits<float>::lowest()) });
267 for (size_t t = 0; t < m_islands.indices.size(); ++t) {
268 const Vec3i32 &tri = m_islands.indices[t];
269 if (tri.minCoeff() < 0 || size_t(tri.maxCoeff()) >= m_islands.uvs.size())
270 continue;
271 const int island = m_islands.vertex_island[size_t(tri[0])];
272 if (island < 0 || size_t(island) >= n_islands)
273 continue;
274 m_island_tris[size_t(island)].push_back(int(t));
275 auto &[lo, hi] = m_island_raw_bounds[size_t(island)];
276 for (int k = 0; k < 3; ++k) {
277 lo = lo.cwiseMin(m_islands.uvs[size_t(tri[k])]);
278 hi = hi.cwiseMax(m_islands.uvs[size_t(tri[k])]);
279 }
280 }
281
282 m_mesh_dirty = true;
283 m_background_quad_dirty = true; // the backdrop is sized to the unwrap, so it moved too
284 Refresh();
285}
std::vector< std::vector< int > > m_island_boundary_verts
Definition UVEditorCanvas.hpp:273

References Slic3r::GUI::UVEditorCanvas::Islands::island_count, m_active_edge, m_active_vertex, m_background_quad_dirty, m_hover_edge, m_hover_island, m_hover_vertex, m_island_boundary_edges, m_island_boundary_verts, m_island_raw_bounds, m_island_tris, m_islands, m_mesh_dirty, m_needs_fit, m_sel_edges, m_sel_vertices, m_selected_island, m_selection, and Slic3r::GUI::UVEditorCanvas::Islands::uvs.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::update_uv_editor().

Here is the caller graph for this function:

◆ set_pane_state()

void Slic3r::GUI::UVEditorCanvas::set_pane_state ( PaneState state)
354{
355 m_pane_state = std::move(state);
357 if (m_on_pane_state)
359}
PaneStateFn m_on_pane_state
Definition UVEditorCanvas.hpp:406

References m_on_pane_state, m_pane_state, and update_status().

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::push_uv_pane_state().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_pane_state_callback()

void Slic3r::GUI::UVEditorCanvas::set_pane_state_callback ( PaneStateFn fn)
inline
150{ m_on_pane_state = std::move(fn); }

References m_on_pane_state.

◆ set_select_mode()

void Slic3r::GUI::UVEditorCanvas::set_select_mode ( SelectMode mode)
557{
558 if (m_select_mode == mode)
559 return;
560 m_select_mode = mode;
561 m_active_vertex = -1;
562 m_active_edge = { -1, -1 };
564 m_hover_edge = { -1, -1 };
565 m_sel_vertices.clear();
566 m_sel_edges.clear();
569 Refresh();
570}

References m_active_edge, m_active_vertex, m_gesture, m_hover_edge, m_hover_island, m_hover_vertex, m_sel_edges, m_sel_vertices, m_select_mode, None, and update_status().

Referenced by run_command(), and Slic3r::GUI::GLGizmoTextureDisplacement::update_uv_editor().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_snap_enabled()

void Slic3r::GUI::UVEditorCanvas::set_snap_enabled ( bool enabled)
inline
103{ m_snap_enabled = enabled; }

References m_snap_enabled.

◆ set_status_callback()

void Slic3r::GUI::UVEditorCanvas::set_status_callback ( StatusFn fn)
inline
155{ m_on_status = std::move(fn); }
StatusFn m_on_status
Definition UVEditorCanvas.hpp:404

References m_on_status.

◆ set_uv_transform()

void Slic3r::GUI::UVEditorCanvas::set_uv_transform ( float tiling_scale,
float rotation_deg,
bool tile_enabled,
bool tile_mirrored )
301{
302 m_tiling_scale = (std::abs(tiling_scale) > 1e-6f) ? tiling_scale : 1.f;
303 m_rotation_deg = rotation_deg;
304 m_tile_enabled = tile_enabled;
305 m_tile_mirrored = tile_mirrored;
307}
float m_rotation_deg
Definition UVEditorCanvas.hpp:296
float m_tiling_scale
Definition UVEditorCanvas.hpp:295

References m_background_quad_dirty, m_rotation_deg, m_tile_enabled, m_tile_mirrored, and m_tiling_scale.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::update_uv_editor().

Here is the caller graph for this function:

◆ set_vertex_edit_callback()

void Slic3r::GUI::UVEditorCanvas::set_vertex_edit_callback ( UVVertexEditFn fn)
inline
180{ m_on_vertex_edit = std::move(fn); }

References m_on_vertex_edit.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::update_uv_editor().

Here is the caller graph for this function:

◆ snap_correction()

Vec2f Slic3r::GUI::UVEditorCanvas::snap_correction ( int island) const
private
672{
673 if (!m_snap_enabled || island < 0 || size_t(island) >= m_island_boundary_verts.size())
674 return Vec2f::Zero();
675
676 // A screen-space threshold, so the magnet feels the same at every zoom level.
677 const wxSize size = GetSize();
678 const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight())));
679 const float threshold = SNAP_PIXELS * uv_per_px;
680
681 float best_dist_sq = threshold * threshold;
682 Vec2f best = Vec2f::Zero();
683 for (const int mine : m_island_boundary_verts[size_t(island)]) {
684 const Vec2f a = island_uv(size_t(mine));
685 for (size_t other = 0; other < m_island_boundary_verts.size(); ++other) {
686 if (int(other) == island)
687 continue;
688 for (const int theirs : m_island_boundary_verts[other]) {
689 const Vec2f d = island_uv(size_t(theirs)) - a;
690 const float dist_sq = d.squaredNorm();
691 if (dist_sq < best_dist_sq) {
692 best_dist_sq = dist_sq;
693 best = d;
694 }
695 }
696 }
697 }
698 return best;
699}

References island_uv(), m_island_boundary_verts, m_snap_enabled, and m_zoom.

Referenced by end_gesture().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ snap_enabled()

bool Slic3r::GUI::UVEditorCanvas::snap_enabled ( ) const
inline
104{ return m_snap_enabled; }

References m_snap_enabled.

◆ update_hover()

void Slic3r::GUI::UVEditorCanvas::update_hover ( const wxPoint & pos)
private
537{
538 int island = -1, vertex = -1;
539 std::pair<int, int> edge{ -1, -1 };
540 if (has_islands()) {
541 const Vec2f uv = screen_to_uv(pos);
542 switch (m_select_mode) {
543 case SelectMode::Island: island = island_at(uv); break;
544 case SelectMode::Vertex: vertex = vertex_at(uv); break;
545 case SelectMode::Edge: edge = edge_at(uv); break;
546 }
547 }
548 if (island != m_hover_island || vertex != m_hover_vertex || edge != m_hover_edge) {
549 m_hover_island = island;
550 m_hover_vertex = vertex;
551 m_hover_edge = edge;
552 Refresh();
553 }
554}

References Edge, edge_at(), has_islands(), Island, island_at(), m_hover_edge, m_hover_island, m_hover_vertex, m_select_mode, screen_to_uv(), Vertex, and vertex_at().

Referenced by on_mouse().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_status()

void Slic3r::GUI::UVEditorCanvas::update_status ( )
private
362{
363 if (!m_on_status)
364 return;
365
366 wxString msg;
367 switch (m_gesture) {
368 case Gesture::MoveIsland: msg = _L("Moving island | release to drop, Esc to cancel"); break;
371 msg = wxString::Format(_L("Rotating island: %d° | Shift = snap 15°, click to confirm, Esc to cancel"),
372 int(std::lround(m_rot_display_deg)));
373 break;
374 case Gesture::ScaleIslandModal: msg = _L("Scaling island | click to confirm, Esc to cancel"); break;
375 case Gesture::MoveVertex: msg = _L("Moving vertex | release to drop"); break;
376 case Gesture::MoveEdge: msg = _L("Moving edge | release to drop"); break;
377 case Gesture::Pan: msg = _L("Panning"); break;
378 case Gesture::None:
379 default:
380 if (!has_islands())
381 msg = m_pane_state.has_layer ? _L("Paint the area on the model, then press Unwrap") :
382 _L("Pick Unwrap as a texture layer's mapping to edit its UVs here");
384 msg = m_sel_vertices.size() > 1 ?
385 wxString::Format(_L_PLURAL("%d vertex selected | drag = move together, Shift/Ctrl click = add/remove",
386 "%d vertices selected | drag = move together, Shift/Ctrl click = add/remove",
387 m_sel_vertices.size()),
388 int(m_sel_vertices.size())) :
389 _L("Vertex mode: drag a vertex to reshape | Shift/Ctrl click = multi-select, wheel = zoom, Home = frame");
391 msg = m_sel_edges.size() > 1 ?
392 wxString::Format(_L_PLURAL("%d edge selected | drag = move together, Shift/Ctrl click = add/remove",
393 "%d edges selected | drag = move together, Shift/Ctrl click = add/remove",
394 m_sel_edges.size()),
395 int(m_sel_edges.size())) :
396 _L("Edge mode: drag an island edge to reshape | Shift/Ctrl click = multi-select, wheel = zoom, Home = frame");
397 else if (m_selection.size() > 1)
398 msg = wxString::Format(_L_PLURAL("%d island selected | drag = move together, Shift/Ctrl click = add/remove, R/S = rotate/scale primary",
399 "%d islands selected | drag = move together, Shift/Ctrl click = add/remove, R/S = rotate/scale primary",
400 m_selection.size()),
401 int(m_selection.size()));
402 else if (m_selected_island >= 0)
403 msg = wxString::Format(_L("Island %d selected | drag = move, R = rotate, S = scale, Shift/Ctrl click = multi-select, Home = frame"),
405 else
406 msg = _L("Click an island to select | Shift/Ctrl click = multi-select, wheel = zoom, middle-drag = pan, Home = frame all");
407 if (m_snap_enabled)
408 msg += _L(" | snap ON");
409 break;
410 }
411 m_on_status(msg);
412}
#define _L(s)
Definition OBJ.cpp:34
#define _L_PLURAL(s, plural, n)
Definition I18N.hpp:96

References _L, _L_PLURAL, Edge, has_islands(), m_gesture, m_on_status, m_pane_state, m_rot_display_deg, m_sel_edges, m_sel_vertices, m_select_mode, m_selected_island, m_selection, m_snap_enabled, MoveEdge, MoveIsland, MoveVertex, None, Pan, RotateIsland, RotateIslandModal, ScaleIslandModal, and Vertex.

Referenced by render(), run_command(), set_pane_state(), and set_select_mode().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ uv_delta_to_unwrap()

Vec2f Slic3r::GUI::UVEditorCanvas::uv_delta_to_unwrap ( const Vec2f & delta_uv) const
private
663{
664 // apply_uv_transform() maps unwrap -> uv as uv = R(unwrap / tiling) + offset, so the inverse of
665 // a *delta* (the translation drops out) is unwrap = tiling * R^-1(delta_uv).
666 const float rad = m_rotation_deg * float(M_PI) / 180.f;
667 const float cs = std::cos(rad), sn = std::sin(rad);
668 return Vec2f(delta_uv.x() * cs + delta_uv.y() * sn, -delta_uv.x() * sn + delta_uv.y() * cs) * m_tiling_scale;
669}
double rad(double deg)
Definition DesignSketchTool.cpp:7748

References M_PI, m_rotation_deg, and m_tiling_scale.

Referenced by end_gesture(), and on_mouse().

Here is the caller graph for this function:

◆ vertex_at()

int Slic3r::GUI::UVEditorCanvas::vertex_at ( const Vec2f & uv) const
private
573{
574 // Screen-space threshold, so the pick feels the same at every zoom.
575 const wxSize size = GetSize();
576 const float uv_per_px = 2.f * m_zoom / float(std::max(1, std::min(size.GetWidth(), size.GetHeight())));
577 const float threshold = SNAP_PIXELS * uv_per_px;
578
579 int best = -1;
580 float best_d = threshold * threshold;
581 for (size_t i = 0; i < m_islands.uvs.size(); ++i) {
582 const float d = (island_uv(i) - uv).squaredNorm();
583 if (d < best_d) {
584 best_d = d;
585 best = int(i);
586 }
587 }
588 return best;
589}

References island_uv(), m_islands, and m_zoom.

Referenced by on_mouse(), and update_hover().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ view_half_extents()

void Slic3r::GUI::UVEditorCanvas::view_half_extents ( float & half_w,
float & half_h ) const
private
472{
473 const wxSize size = GetSize();
474 const float w = float(std::max(1, size.GetWidth()));
475 const float h = float(std::max(1, size.GetHeight()));
476
477 // m_zoom is the half-extent visible across the *shorter* edge; the longer edge shows
478 // proportionally more. Both axes have to be driven off the same uniform scale, or the content
479 // comes out squashed on one of the two pane orientations.
480 const float aspect = w / h;
481 half_w = m_zoom * std::max(aspect, 1.f);
482 half_h = m_zoom * std::max(1.f / aspect, 1.f);
483}

References m_zoom.

Referenced by rebuild_grid(), render(), and screen_to_uv().

Here is the caller graph for this function:

Member Data Documentation

◆ m_active_edge

std::pair<int, int> Slic3r::GUI::UVEditorCanvas::m_active_edge { -1, -1 }
private
336{ -1, -1 };

Referenced by end_gesture(), on_mouse(), render(), set_islands(), and set_select_mode().

◆ m_active_vertex

int Slic3r::GUI::UVEditorCanvas::m_active_vertex = -1
private

◆ m_background_dirty

bool Slic3r::GUI::UVEditorCanvas::m_background_dirty = false
private

◆ m_background_glmodel

GLModel Slic3r::GUI::UVEditorCanvas::m_background_glmodel
private

Referenced by rebuild_background_quad(), and render().

◆ m_background_height

int Slic3r::GUI::UVEditorCanvas::m_background_height = 0
private

◆ m_background_pixels

std::vector<unsigned char> Slic3r::GUI::UVEditorCanvas::m_background_pixels
private

◆ m_background_quad_dirty

bool Slic3r::GUI::UVEditorCanvas::m_background_quad_dirty = true
private

◆ m_background_texture

GLTexture Slic3r::GUI::UVEditorCanvas::m_background_texture
private

◆ m_background_width

int Slic3r::GUI::UVEditorCanvas::m_background_width = 0
private

◆ m_context

wxGLContext* Slic3r::GUI::UVEditorCanvas::m_context = nullptr
private

Referenced by UVEditorCanvas(), and render().

◆ m_cursor_inside

bool Slic3r::GUI::UVEditorCanvas::m_cursor_inside = false
private

Referenced by on_leave(), on_mouse(), and render().

◆ m_cursor_px

wxPoint Slic3r::GUI::UVEditorCanvas::m_cursor_px { 0, 0 }
private
355{ 0, 0 };

Referenced by on_mouse(), and render().

◆ m_dial_glmodel

GLModel Slic3r::GUI::UVEditorCanvas::m_dial_glmodel
private

Referenced by rebuild_rotation_dial(), and render().

◆ m_dim_quad_glmodel

GLModel Slic3r::GUI::UVEditorCanvas::m_dim_quad_glmodel
private

Referenced by render().

◆ m_drag_last_px

wxPoint Slic3r::GUI::UVEditorCanvas::m_drag_last_px
private

Referenced by on_mouse().

◆ m_gesture

◆ m_gesture_last_angle

float Slic3r::GUI::UVEditorCanvas::m_gesture_last_angle = 0.f
private

Referenced by on_key(), and on_mouse().

◆ m_gesture_last_dist

float Slic3r::GUI::UVEditorCanvas::m_gesture_last_dist = 0.f
private

Referenced by on_key(), and on_mouse().

◆ m_gesture_last_uv

Vec2f Slic3r::GUI::UVEditorCanvas::m_gesture_last_uv = Vec2f::Zero()
private

Referenced by on_mouse().

◆ m_gl_reset_pending

bool Slic3r::GUI::UVEditorCanvas::m_gl_reset_pending = false
private

Referenced by invalidate_gl(), and render().

◆ m_grid_glmodel

GLModel Slic3r::GUI::UVEditorCanvas::m_grid_glmodel
private

Referenced by rebuild_grid(), and render().

◆ m_grid_step

float Slic3r::GUI::UVEditorCanvas::m_grid_step = 0.f
private

Referenced by rebuild_grid().

◆ m_hover_edge

std::pair<int, int> Slic3r::GUI::UVEditorCanvas::m_hover_edge { -1, -1 }
private
365{ -1, -1 };

Referenced by on_leave(), render(), set_islands(), set_select_mode(), and update_hover().

◆ m_hover_island

int Slic3r::GUI::UVEditorCanvas::m_hover_island = -1
private

◆ m_hover_vertex

int Slic3r::GUI::UVEditorCanvas::m_hover_vertex = -1
private

◆ m_island_boundary_edges

std::vector<std::vector<std::pair<int, int> > > Slic3r::GUI::UVEditorCanvas::m_island_boundary_edges
private

Referenced by render(), and set_islands().

◆ m_island_boundary_verts

std::vector<std::vector<int> > Slic3r::GUI::UVEditorCanvas::m_island_boundary_verts
private

Referenced by set_islands(), and snap_correction().

◆ m_island_fill

std::vector<GLModel> Slic3r::GUI::UVEditorCanvas::m_island_fill
private

Referenced by rebuild_island_models(), and render().

◆ m_island_fill_colors

std::vector<ColorRGBA> Slic3r::GUI::UVEditorCanvas::m_island_fill_colors
private

Referenced by render(), and set_island_fill_colors().

◆ m_island_raw_bounds

std::vector<std::pair<Vec2f, Vec2f> > Slic3r::GUI::UVEditorCanvas::m_island_raw_bounds
private

◆ m_island_tris

std::vector<std::vector<int> > Slic3r::GUI::UVEditorCanvas::m_island_tris
private

Referenced by island_at(), and set_islands().

◆ m_island_wireframe

std::vector<GLModel> Slic3r::GUI::UVEditorCanvas::m_island_wireframe
private

Referenced by rebuild_island_models(), and render().

◆ m_islands

◆ m_mesh_dirty

bool Slic3r::GUI::UVEditorCanvas::m_mesh_dirty = true
private

◆ m_modal_scale_accum

float Slic3r::GUI::UVEditorCanvas::m_modal_scale_accum = 1.f
private

◆ m_needs_fit

bool Slic3r::GUI::UVEditorCanvas::m_needs_fit = true
private

◆ m_on_command

CommandFn Slic3r::GUI::UVEditorCanvas::m_on_command
private

◆ m_on_island_edit

IslandEditFn Slic3r::GUI::UVEditorCanvas::m_on_island_edit
private

◆ m_on_pane_state

PaneStateFn Slic3r::GUI::UVEditorCanvas::m_on_pane_state
private

◆ m_on_status

StatusFn Slic3r::GUI::UVEditorCanvas::m_on_status
private

◆ m_on_vertex_edit

UVVertexEditFn Slic3r::GUI::UVEditorCanvas::m_on_vertex_edit
private

◆ m_pan

Vec2f Slic3r::GUI::UVEditorCanvas::m_pan = Vec2f(0.5f, 0.5f)
private

◆ m_pane_state

PaneState Slic3r::GUI::UVEditorCanvas::m_pane_state
private

◆ m_rot_applied_deg

float Slic3r::GUI::UVEditorCanvas::m_rot_applied_deg = 0.f
private

◆ m_rot_base_deg

float Slic3r::GUI::UVEditorCanvas::m_rot_base_deg = 0.f
private

Referenced by on_key(), and on_mouse().

◆ m_rot_display_deg

float Slic3r::GUI::UVEditorCanvas::m_rot_display_deg = 0.f
private

◆ m_rot_raw_deg

float Slic3r::GUI::UVEditorCanvas::m_rot_raw_deg = 0.f
private

◆ m_rotation_deg

float Slic3r::GUI::UVEditorCanvas::m_rotation_deg = 0.f
private

◆ m_sel_edges

std::vector<std::pair<int, int> > Slic3r::GUI::UVEditorCanvas::m_sel_edges
private

◆ m_sel_vertices

std::vector<int> Slic3r::GUI::UVEditorCanvas::m_sel_vertices
private

◆ m_select_mode

SelectMode Slic3r::GUI::UVEditorCanvas::m_select_mode = SelectMode::Island
private

◆ m_selected_island

int Slic3r::GUI::UVEditorCanvas::m_selected_island = -1
private

◆ m_selection

std::vector<int> Slic3r::GUI::UVEditorCanvas::m_selection
private

◆ m_snap_enabled

bool Slic3r::GUI::UVEditorCanvas::m_snap_enabled = false
private

◆ m_stencil_bits

int Slic3r::GUI::UVEditorCanvas::m_stencil_bits = -1
private

Referenced by render().

◆ m_stroke_glmodel

GLModel Slic3r::GUI::UVEditorCanvas::m_stroke_glmodel
private

Referenced by render().

◆ m_tile_enabled

bool Slic3r::GUI::UVEditorCanvas::m_tile_enabled = true
private

◆ m_tile_mirrored

bool Slic3r::GUI::UVEditorCanvas::m_tile_mirrored = false
private

Referenced by render(), and set_uv_transform().

◆ m_tile_outline_glmodel

GLModel Slic3r::GUI::UVEditorCanvas::m_tile_outline_glmodel
private

Referenced by render().

◆ m_tiling_scale

float Slic3r::GUI::UVEditorCanvas::m_tiling_scale = 1.f
private

◆ m_transforms

std::vector<IslandTransform> Slic3r::GUI::UVEditorCanvas::m_transforms
private

◆ m_vertex_edit_moved

bool Slic3r::GUI::UVEditorCanvas::m_vertex_edit_moved = false
private

◆ m_vertex_marker_glmodel

GLModel Slic3r::GUI::UVEditorCanvas::m_vertex_marker_glmodel
private

Referenced by render().

◆ m_zoom

float Slic3r::GUI::UVEditorCanvas::m_zoom = 0.75f
private

The documentation for this class was generated from the following files: