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

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

Inheritance diagram for Slic3r::GUI::GLGizmoTextureDisplacement:
Collaboration diagram for Slic3r::GUI::GLGizmoTextureDisplacement:

Classes

struct  MixTarget
struct  PreviewColorRun
struct  PartPreview
struct  PaintedColors
struct  IslandViewFollow
struct  LayerPaintMap
struct  LibraryTexture
struct  UVEditorState
struct  TipMedia

Public Types

using PaletteEntry = PrintableColor
enum class  EGrabberExtension {
  None = 0 , PosX = 1 << 0 , NegX = 1 << 1 , PosY = 1 << 2 ,
  NegY = 1 << 3 , PosZ = 1 << 4 , NegZ = 1 << 5
}
enum  EState { Off , On , Num_States }

Public Member Functions

 GLGizmoTextureDisplacement (GLCanvas3D &parent, const std::string &icon_filename, unsigned int sprite_id)
TextureColorSettings color_settings_for (const ModelVolume &mv)
const std::vector< PaletteEntry > & cached_palette ()
std::pair< ColorQuantizeFn, ColorQuantizeFn > palette_quantizers ()
const ModelVolume * preview_part (const PartPreview &preview) const
bool has_true_preview () const
void render_painter_gizmo () override
bool on_mouse (const wxMouseEvent &mouse_event) override
 Implement when want to process mouse events in gizmo Click, Right click, move, drag, ...
void data_changed (bool is_serializing) override
 Is called when data (Selection) is changed.
virtual bool gizmo_event (SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down)
virtual bool on_tool_shortcut (Shortcut shortcut)
virtual const float get_cursor_radius_min () const
virtual const float get_cursor_radius_max () const
virtual const float get_cursor_radius_step () const
virtual const float get_cursor_height_min () const
virtual const float get_cursor_height_max () const
virtual const float get_cursor_height_step () const
bool init ()
void load (cereal::BinaryInputArchive &ar)
void save (cereal::BinaryOutputArchive &ar) const
std::string get_name (bool include_shortcut=true) const
EState get_state () const
void set_state (EState state)
std::optional< Shortcut > shortcut () const
const std::string & get_icon_filename () const
void set_icon_filename (const std::string &filename)
bool is_activable () const
bool is_selectable () const
CommonGizmosDataID get_requirements () const
void set_common_data_pool (CommonGizmosDataPool *ptr)
virtual bool apply_clipping_plane ()
unsigned int get_sprite_id () const
int get_hover_id () const
void set_hover_id (int id)
bool is_dragging () const
bool update_items_state ()
void render ()
void render_input_window (float x, float y, float bottom_limit)
virtual void on_change_color_mode (bool is_dark)
virtual std::string get_tooltip () const
 Mouse tooltip text.
int get_count ()
std::string get_gizmo_name ()
void register_raycasters_for_picking ()
void unregister_raycasters_for_picking ()
virtual bool is_in_editing_mode () const
virtual bool is_selection_rectangle_dragging () const

Static Public Member Functions

static TextureDisplacementPrepareResult prepare_mesh (const indexed_triangle_set &base, const TextureDisplacementFacetsData &masks, const std::vector< TextureDisplacementLayer > &layers, const std::vector< Vec2f > &model_uv, const Transform3d &texture_frame, const TextureDisplacementPrepareParams &params, const TextureColorSettings &color_settings, const DisplacementProgressFn &progress, BakeStageRecorder *debug=nullptr)
static TextureDisplacementFacetsData facets_data_of (const ModelVolume &mv)
static std::vector< MixTarget > mix_targets (const std::vector< TextureDisplacementLayer > &layers)
static std::vector< PaletteEntry > rank_mixes (const std::vector< ColorRGBA > &filaments, const std::vector< MixTarget > &targets, int limit)
static std::vector< PaletteEntry > pick_mixes (const std::vector< PaletteEntry > &ranking, int count, int free_slots, const std::function< bool(const PaletteEntry &)> &reusable)
static std::vector< PaletteEntry > make_palette (const std::vector< ColorRGBA > &filaments, const std::vector< PaletteEntry > &mixes)
static ColorQuantizeFn make_palette_quantizer (const std::vector< PaletteEntry > &palette)
static std::vector< int > palette_filaments (const std::vector< PaletteEntry > &palette, const std::vector< uint8_t > &triangle_color, const std::function< int(const PaletteEntry &)> &slot_for_mix)
static std::vector< ColorRGBA > filament_palette ()
static indexed_triangle_set group_preview_by_color (const indexed_triangle_set &its, const std::vector< uint8_t > &triangle_color, const std::vector< ColorRGBA > &entry_colors, std::vector< PreviewColorRun > &runs)
static bool any_layer_colors (const ModelVolume &mv)
static PaintedColors painted_colors (const TriangleMesh &mesh, const TriangleSelector::TriangleSplittingData &paint)
static void update_render_colors ()
static void load_render_colors ()

Public Attributes

std::vector< PaletteEntry > m_palette_cache
ColorQuantizeFn m_palette_quantizer
ColorQuantizeFn m_palette_pure_quantizer
bool m_palette_changed = false
std::vector< ColorRGBA > m_palette_filaments
std::vector< std::shared_ptr< std::vector< unsigned char > > > m_palette_images
std::optional< std::vector< PaletteEntry > > m_mix_ranking
bool m_palette_mixing = false
int m_palette_mix_count = 0
int m_palette_free_slots = 0
std::vector< std::string > m_palette_slots
std::vector< PreviewColorRun > m_preview_color_runs
std::vector< std::unique_ptr< PartPreview > > m_part_previews

Static Public Attributes

static const unsigned int BASE_ID = 255 * 255 * 254
static const unsigned int GRABBER_ELEMENTS_MAX_COUNT = 7
static float INV_ZOOM = 1.0f
static ColorRGBA DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f }
static ColorRGBA DEFAULT_DRAG_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }
static ColorRGBA DEFAULT_HIGHLIGHT_COLOR = {1.0f, 0.38f, 0.0f, 1.0f}
static std::array< ColorRGBA, 3 > AXES_COLOR
static std::array< ColorRGBA, 3 > AXES_HOVER_COLOR
static ColorRGBA CONSTRAINED_COLOR = {0.5f, 0.5f, 0.5f, 1.0f}
static ColorRGBA FLATTEN_COLOR = {0.96f, 0.93f, 0.93f, 0.5f}
static ColorRGBA FLATTEN_HOVER_COLOR = {1.0f, 1.0f, 1.0f, 0.75f}
static ColorRGBA GRABBER_NORMAL_COL = {1.0f, 1.0f, 1.0f, 1.0f}
static ColorRGBA GRABBER_HOVER_COL = {0.863f, 0.125f, 0.063f, 1.0f}
static ColorRGBA GRABBER_UNIFORM_COL = {0, 1.0, 1.0, 1.0f}
static ColorRGBA GRABBER_UNIFORM_HOVER_COL = {0, 0.7, 0.7, 1.0f}

Protected Types

enum class  ToolType { BRUSH , BUCKET_FILL , SMART_FILL , GAP_FILL }
enum class  Button { None , Left , Right }

Protected Member Functions

void on_render_input_window (float x, float y, float bottom_limit) override
std::string on_get_name () const override
bool on_is_activable () const override
wxString handle_snapshot_action_name (bool shift_down, ButtonButton button_down) const override
std::string get_gizmo_entering_text () const override
std::string get_gizmo_leaving_text () const override
std::string get_action_snapshot_name () const override
EnforcerBlockerType get_left_button_state_type () const override
EnforcerBlockerType get_right_button_state_type () const override
virtual void render_triangles (const Selection &selection, const ModelVolume *skip=nullptr) const
void render_triangles (const Selection &selection, const std::vector< const ModelVolume * > &skip) const
void render_cursor ()
void render_cursor_circle ()
void render_cursor_sphere (const Transform3d &trafo) const
void render_cursor_height_range (const Transform3d &trafo) const
virtual ColorRGBA get_cursor_sphere_left_button_color () const
virtual ColorRGBA get_cursor_sphere_right_button_color () const
virtual ColorRGBA get_cursor_hover_color () const
Vec3f get_tilt_up_direction () const
ClippingPlaneDataWrapper get_clipping_plane_data () const
TriangleSelector::ClippingPlane get_clipping_plane_in_volume_coordinates (const Transform3d &trafo) const
bool is_painting () const
void on_set_state () override
bool render_follows_cursor () const override
bool on_is_selectable () const override
void on_load (cereal::BinaryInputArchive &ar) override
void on_save (cereal::BinaryOutputArchive &ar) const override
CommonGizmosDataID on_get_requirements () const override
bool wants_enter_leave_snapshots () const override
bool render_combo (const std::string &label, const std::vector< std::string > &lines, int &selection_idx, float label_width, float item_width)
void render_cross_mark (const Vec3f &target, bool is_single=false)
virtual void on_set_hover_id ()
virtual void on_enable_grabber (unsigned int id)
virtual void on_disable_grabber (unsigned int id)
virtual void on_start_dragging ()
virtual void on_stop_dragging ()
virtual void on_dragging (const UpdateData &data)
bool GizmoImguiBegin (const std::string &name, int flags)
void GizmoImguiEnd ()
void GizmoImguiSetNextWIndowPos (float &x, float y, int flag, float pivot_x=0.0f, float pivot_y=0.0f)
void GizmoImguiSetNextWIndowPos (float &x, float y, float w, float h, int flag, float pivot_x=0.0f, float pivot_y=0.0f)
void register_grabbers_for_picking ()
void unregister_grabbers_for_picking ()
virtual void on_register_raycasters_for_picking ()
virtual void on_unregister_raycasters_for_picking ()
void render_grabbers (const BoundingBoxf3 &box) const
void render_grabbers (float size) const
void render_grabbers (size_t first, size_t last, float size, bool force_hover) const
std::string format (float value, unsigned int decimals) const
void set_dirty ()
bool use_grabbers (const wxMouseEvent &mouse_event)
 function which Set up m_dragging and call functions on_start_dragging / on_dragging / on_stop_dragging
void do_stop_dragging (bool perform_mouse_cleanup)

Protected Attributes

float m_cursor_radius = 1.f
float m_cursor_height = 0.2f
std::vector< std::unique_ptr< TriangleSelectorGUI > > m_triangle_selectors
TriangleSelector::CursorType m_cursor_type = TriangleSelector::SPHERE
bool m_triangle_splitting_enabled = true
ToolType m_tool_type = ToolType::BRUSH
float m_smart_fill_angle = 30.f
bool m_paint_on_overhangs_only = false
float m_highlight_by_angle_threshold_deg = 0.f
GLModel m_circle
Vec2d m_old_center { Vec2d::Zero() }
float m_old_cursor_radius { 0.0f }
bool m_vertical_only = false
bool m_horizontal_only = false
int m_seed_fill_last_mesh_id = -1
GLCanvas3D & m_parent
int m_group_id
EState m_state
std::optional< Shortcut > m_shortcut
std::string m_icon_filename
unsigned int m_sprite_id
int m_hover_id { -1 }
bool m_dragging { false }
std::vector< Grabber > m_grabbers
ImGuiWrapper * m_imgui
bool m_first_input_window_render { true }
CommonGizmosDataPool * m_c { nullptr }
bool m_is_dark_mode = false
float last_input_window_width = 0

Static Protected Attributes

static constexpr float CursorRadiusMin = 0.4f
static constexpr float CursorRadiusMax = 8.f
static constexpr float CursorRadiusStep = 0.2f
static constexpr float CursorHeightMin = 0.1f
static constexpr float CursorHeightMax = 8.f
static constexpr float CursorHeightStep = 0.2f
static constexpr float SmartFillAngleMin = 0.0f
static constexpr float SmartFillAngleMax = 90.f
static constexpr float SmartFillAngleStep = 1.f

Private Types

enum class  PrepareScope { PaintedParts , AllParts }
enum class  UVCheckMode { None , Checker , Distortion }
enum class  UVBackground { None , Height , Checker }
enum class  AdjustHandle { None , Pan , AxisU , AxisV }

Private Member Functions

bool on_init () override
void update_model_object () override
void update_from_model_object (bool first_update) override
void on_opening () override
void on_shutdown () override
PainterGizmoType get_painter_type () const override
ModelVolume * texture_volume ()
const ModelVolume * texture_volume () const
std::vector< ModelVolume * > texture_parts ()
std::vector< ModelVolume * > painted_parts ()
bool any_part_painted () const
std::vector< TextureDisplacementLayer > layers_for_part (const ModelVolume &part) const
Vec3d texture_anchor () const
std::vector< float > resting_drops (const std::vector< ModelVolume * > &parts)
Transform3d prepare_texture_frame (const ModelVolume &part)
void add_texture_layer ()
void remove_texture_layer (int slot)
void move_texture_layer (int slot, int to_index)
void swap_layer_slots (int slot_a, int slot_b)
void set_active_layer (int slot)
void bake (bool own_snapshot=true)
bool pro_mode () const
bool apply_standard_mode_presets (ModelVolume *mv)
void store_panel_state ()
void bake_standard ()
void queue_prepare (const TextureDisplacementPrepareParams &params, const std::string &snapshot_name, bool then_bake, const std::string &unchanged_msg, PrepareScope scope=PrepareScope::PaintedParts)
void select_whole_model ()
int texture_volume_raycaster_index () const
int select_visible_faces (const std::array< float, 12 > *uv_clip=nullptr)
void show_projector (bool show)
void update_projector ()
int apply_projection_frame ()
void subdivide_model ()
GLTexture * get_layer_thumbnail (const TextureDisplacementLayer &layer)
GLTexture * get_layer_height_texture (const TextureDisplacementLayer &layer)
GLTexture * get_layer_color_texture (const TextureDisplacementLayer &layer)
const LibraryTexture * get_library_texture (const std::string &path)
void render_texture_library_popup (const ImVec2 &panel_min, const ImVec2 &panel_max)
void set_layer_texture (TextureDisplacementLayer &layer, const TextureLibraryEntry &entry)
void import_custom_texture (TextureDisplacementLayer &layer)
void render_image_adjust_window (const ImVec2 &panel_min, const ImVec2 &panel_max)
void open_image_adjust (const TextureDisplacementLayer &layer)
bool load_image_adjust_source (const TextureDisplacementLayer &layer)
void commit_image_adjust (TextureDisplacementLayer &layer)
void close_image_adjust ()
void update_image_adjust_preview ()
void invalidate_adjust_textures ()
bool update_adjust_anchor ()
bool on_mouse_adjust_texture (const wxMouseEvent &mouse_event)
void render_adjust_texture_gizmo ()
void render_paint_cursor_hint ()
void adjust_tangent_basis (Vec3f &u_axis, Vec3f &v_axis) const
Vec3f adjust_plane_point () const
Vec3f adjust_handle_center (const TextureDisplacementLayer &layer) const
TextureDisplacementLayer * active_layer ()
const TextureDisplacementLayer * active_layer () const
void rebuild_preview ()
void render_preview_mesh ()
void rebuild_shaded_preview_mesh ()
void render_shaded_preview_mesh ()
void update_uv_editor ()
void on_island_edited (int island, const Vec2f &offset_delta, float rotation_delta, float scale_factor, bool finished)
void on_uv_vertex_edited (const std::vector< std::pair< int, Vec2f > > &edits)
std::vector< Eigen::Matrix< float, 2, 3 > > uv_editor_island_transforms (const TextureDisplacementLayer &layer)
void on_uv_command (int cmd, float value)
void process_uv_commands ()
void run_uv_command (int cmd, float value)
void push_uv_pane_state ()
void apply_view_mode (int mode)
int view_mode () const
void follow_island_highlight ()
void cut_island (TextureDisplacementLayer &layer, int chart)
void capture_view_projection (TextureDisplacementLayer &layer)
bool on_mouse_seam (const wxMouseEvent &mouse_event)
void toggle_seam_at (const Vec2d &mouse_pos)
void rebuild_seam_overlay ()
void render_seam_overlay ()
std::pair< int, int > seam_edge_at (const Vec2d &mouse_pos) const
void rebuild_seam_hover_overlay ()
int seam_vertex_at (const Vec2d &mouse_pos) const
void mark_seam_path (int v_from, int v_to)
void rebuild_seam_anchor_overlay ()
void rebuild_subdivide_preview ()
void render_subdivide_preview ()
void subdivide_model_adaptive ()
void smooth_model ()
void remesh_model ()
double painted_area_mm2 (const ModelVolume &mv)
size_t estimated_refined_triangles (const ModelVolume &mv, float edge_mm)
const V2Resolution & v2_recommendation (const ModelVolume &mv)
void run_stage_debug ()
void show_debug_stage (int index)
void exit_debug_view ()
void render_debug_stage_panel (ModelVolume *mv)
const std::vector< int > * island_highlight_selection ()
void rebuild_island_overlay (const std::vector< int > &selection)
void render_island_overlay ()
void rebuild_paint_overlay ()
void render_paint_overlay (GLModel &overlay)
void rebuild_other_paint_overlay ()
void rebuild_painted_colors (bool whole_stack)
bool render_painted_colors (bool whole_stack)
bool shaded_preview_ready () const
void compute_shaded_active_faces (const std::vector< int > &charts, size_t patch_face_count)
std::vector< int > build_island_move_set (const TextureDisplacementLayer &layer, int primary) const
std::vector< Vec2f > compute_layer_vertex_uvs (const indexed_triangle_set &patch, const TextureDisplacementLayer &layer, const std::vector< int > *patch_source=nullptr) const
std::vector< Vec2f > compute_layer_corner_uvs (const indexed_triangle_set &patch, const TextureDisplacementLayer &layer, const std::vector< int > *patch_source=nullptr) const
std::vector< Vec2f > model_uv_corners (const indexed_triangle_set &local_patch, const TextureDisplacementLayer &layer, const std::vector< int > *patch_source) const
int layer_projection_frame (const indexed_triangle_set &local_patch, const TextureDisplacementLayer &layer, Vec3f &center, Vec3f &axis) const
indexed_triangle_set patch_in_world (const indexed_triangle_set &patch) const
void rebuild_uvcheck_mesh ()
void render_uvcheck_mesh ()
void compute_uv_editor_distortion_colors (const indexed_triangle_set &patch)
void rebuild_wireframe_overlay ()
void build_wireframe_from_its (const indexed_triangle_set &its)
void refresh_wireframe ()
void render_wireframe_overlay ()
void queue_preview_job ()
void ease_wheel_scroll (float &target, float &applied)
void ensure_panel_icons ()
void panel_tooltip (const std::string &text, const std::vector< TipMedia > &media={})
void panel_tooltip (const wxString &text, const std::vector< TipMedia > &media={})
void render_tip_media (const std::vector< TipMedia > &media, float width)
void on_render () override
std::vector< std::vector< ProjectedMousePosition > > get_projected_mouse_positions (const Vec2d &mouse_position, double resolution, const std::vector< Transform3d > &trafo_matrices) const
std::vector< ProjectedHeightRange > get_projected_height_range (const Vec2d &mouse_position, double resolution, const std::vector< const ModelVolume * > &part_volumes, const std::vector< Transform3d > &trafo_matrices) const
bool is_mesh_point_clipped (const Vec3d &point, const Transform3d &trafo) const
std::vector< Transform3d > mesh_trafo_matrices () const
void update_raycast_cache (const Vec2d &mouse_position, const Camera &camera, const std::vector< Transform3d > &trafo_matrices) const
BoundingBoxf3 bounding_box () const
void update_contours (int i, const TriangleMesh &vol_mesh, float cursor_z, float max_z, float min_z) const

Static Private Member Functions

static TextureDisplacementFacetsData masks_after_subdivision (const TriangleMesh &new_mesh, const std::vector< int > &source, const std::array< LayerPaintMap, TEXTURE_DISPLACEMENT_MAX_LAYERS > &paint)
static bool plan_remesh (const indexed_triangle_set &src, float target_edge_mm, float sharp_angle_deg, indexed_triangle_set &out)
static float layer_texture_aspect (const TextureDisplacementLayer &layer)
static bool collect_paint_region (const TriangleMesh &mesh, const TextureDisplacementFacetsData &facets, std::vector< uint8_t > &region, std::array< LayerPaintMap, TEXTURE_DISPLACEMENT_MAX_LAYERS > *paint)
static int island_group_of (const std::vector< int > &groups, int c)
static void join_island_groups (std::vector< int > &groups, int a, int b, int chart_count)

Private Attributes

int m_panel_mode = 0
bool m_prepare_in_progress = false
bool m_project_only_visible = false
TextureProjectorFrame * m_projector_frame = nullptr
int m_projector_opacity = 140
const void * m_projector_tex_source = nullptr
float m_projector_tex_smoothing = -1.f
int m_picker_slot = -1
bool m_picker_open_request = false
bool m_picker_reveal_selected = false
float m_picker_scroll_target = 0.f
float m_picker_scroll_applied = -1.f
bool m_erase_mode = false
std::array< bool, TEXTURE_DISPLACEMENT_MAX_LAYERS > m_layer_expanded {}
std::string m_slider_type_request
int m_slider_type_request_frame = 0
int m_adjust_slot = -1
TextureAdjustments m_adjust
TextureAdjustments m_adjust_applied
DecodedHeightTexture m_adjust_source
DecodedHeightTexture m_adjust_preview_source
const void * m_adjust_source_image = nullptr
size_t m_adjust_volume_id = 0
std::unique_ptr< GLTexture > m_adjust_preview_tex
std::future< DecodedHeightTexture > m_adjust_preview_job
TextureAdjustments m_adjust_preview_running
TextureAdjustments m_adjust_preview_shown
bool m_adjust_preview_valid = false
int m_uv_select_mode = 0
std::vector< std::pair< int, float > > m_uv_command_queue
IslandViewFollow m_island_view
const void * m_uv_thumb_source = nullptr
std::vector< unsigned char > m_uv_thumb_rgb
bool m_seam_edit_mode = false
GLModel m_seam_glmodel
std::pair< int, int > m_seam_hover_edge { -1, -1 }
int m_seam_hover_vertex = -1
GLModel m_seam_hover_glmodel
bool m_seam_path_mode = false
int m_seam_path_anchor = -1
GLModel m_seam_anchor_glmodel
bool m_island_drag_active = false
int m_active_layer_slot = 0
bool m_selectors_stale = false
bool m_bake_in_progress = false
bool m_auto_update = true
int m_subdivide_count = 1
bool m_subdivide_editing = false
int m_subdivide_preview_tris = -1
GLModel m_subdivide_preview_glmodel
bool m_subdivide_adaptive = false
float m_subdivide_target_mm = 0.f
bool m_subdivide_feature = false
float m_subdivide_detail_mm = 0.05f
float m_subdivide_min_edge_mm = 0.1f
float m_subdivide_border_mm = 0.4f
float m_subdivide_color_mm = 0.3f
int m_subdivide_budget_k = 750
float m_remesh_target_edge_mm = 0.f
float m_remesh_sharp_angle_deg = 40.f
bool m_remesh_keep_sharp_edges = true
std::vector< BakeStageSnapshot > m_debug_stages
int m_debug_stage = -1
bool m_debug_in_progress = false
bool m_debug_check_topology = true
double m_painted_area_mm2 = 0.
std::string m_painted_area_key
V2Resolution m_v2_rec
std::string m_v2_rec_key
GLModel m_preview_glmodel
bool m_preview_params_dirty = false
bool m_use_shaded_preview = false
bool m_shaded_preview_dirty = false
GLModel m_shaded_preview_glmodel
GLModel m_paint_overlay_glmodel
GLModel m_island_overlay_glmodel
std::vector< int > m_island_overlay_selection
bool m_paint_overlay_dirty = false
GLModel m_other_paint_glmodel
std::string m_other_paint_key
PaintedColors m_painted_colors
std::string m_painted_colors_key
GLModel m_painted_colors_glmodel
std::string m_painted_colors_drawn_key
std::vector< std::pair< int, std::pair< size_t, size_t > > > m_painted_colors_runs
bool m_shaded_preview_uses_vertex_uv = false
int m_shaded_projection_mode = 0
Vec3f m_shaded_patch_center = Vec3f::Zero()
Vec3f m_shaded_patch_axis = Vec3f::UnitZ()
std::vector< PaletteEntry > m_shaded_preview_palette
int m_shaded_active_chart = -1
std::vector< uint8_t > m_shaded_active_face
Eigen::Matrix< float, 2, 3 > m_shaded_baked_active_xf = Eigen::Matrix<float, 2, 3>::Identity()
Eigen::Matrix< float, 2, 3 > m_shaded_island_delta = Eigen::Matrix<float, 2, 3>::Identity()
std::vector< int > m_island_move_set
UVCheckMode m_uv_check_mode = UVCheckMode::None
GLModel m_uvcheck_glmodel
bool m_uvcheck_uses_vertex_uv = false
int m_uvcheck_projection_mode = 0
Vec3f m_uvcheck_patch_center = Vec3f::Zero()
Vec3f m_uvcheck_patch_axis = Vec3f::UnitZ()
bool m_show_uv_editor = false
bool m_uv_unwrap_pending = false
bool m_uv_apply_connected_net = false
size_t m_uv_overrides_sig = 0
UVBackground m_uv_editor_bg = UVBackground::None
float m_uv_editor_bg_smoothing = -1.f
TextureAdjustments m_uv_editor_bg_adjust
const void * m_uv_editor_bg_image = nullptr
std::vector< ColorRGBA > m_uv_editor_distortion_colors
bool m_wireframe_overlay = false
GLModel m_wireframe_overlay_glmodel
size_t m_wireframe_overlay_vcount = 0
indexed_triangle_set m_preview_its
std::shared_ptr< std::atomic< uint64_t > > m_preview_generation = std::make_shared<std::atomic<uint64_t>>(0)
bool m_preview_job_running = false
bool m_preview_job_pending = false
std::array< std::unique_ptr< GLTexture >, TEXTURE_DISPLACEMENT_MAX_LAYERS > m_thumbnails
std::array< std::weak_ptr< const std::vector< unsigned char > >, TEXTURE_DISPLACEMENT_MAX_LAYERS > m_thumbnail_source
std::array< float, TEXTURE_DISPLACEMENT_MAX_LAYERS > m_thumbnail_smoothing {}
std::unique_ptr< GLTexture > m_height_tex
std::weak_ptr< const std::vector< unsigned char > > m_height_tex_source
float m_height_tex_smoothing = -1.f
std::unique_ptr< GLTexture > m_color_tex
std::weak_ptr< const std::vector< unsigned char > > m_color_tex_source
float m_color_tex_smoothing = -1.f
std::map< std::string, LibraryTexture > m_library_textures
uint64_t m_library_generation = 0
UVEditorState m_uv_editor_state
Vec2f m_uv_editor_bbox_min = Vec2f::Zero()
Vec2f m_uv_editor_bbox_max = Vec2f::Zero()
PatchUnwrap m_uv_editor_unwrap
bool m_undocked = false
bool m_show_resolution = false
float m_panel_scroll_target = 0.f
float m_panel_scroll_applied = -1.f
float m_panel_body_h = 0.f
float m_panel_footer_h = 0.f
bool m_adjust_texture_mode = false
bool m_adjust_anchor_valid = false
Vec3f m_adjust_anchor_pos = Vec3f::Zero()
Vec3f m_adjust_anchor_normal = Vec3f::UnitZ()
AdjustHandle m_adjust_drag_handle = AdjustHandle::None
Vec2f m_adjust_drag_start_offset = Vec2f::Zero()
Vec2f m_adjust_drag_start_planar = Vec2f::Zero()
GLModel m_adjust_panel_glmodel
GLModel m_adjust_arrow_glmodel
std::map< std::string, wxString > m_desc
std::vector< std::pair< wxString, wxString > > m_shortcuts_brush
std::vector< std::pair< wxString, wxString > > m_shortcuts_bucket_fill
std::vector< std::pair< wxString, wxString > > m_shortcuts_gap_fill
std::vector< std::pair< wxString, wxString > > m_shortcuts_sliders
IconManager m_panel_icons
std::map< std::string, IconManager::Icons > m_panel_icon_map
bool m_panel_icons_tried = false
ImVec2 m_tooltip_anchor_min {0.f, 0.f}
ImVec2 m_tooltip_anchor_max {0.f, 0.f}
AnimatedTextureCache m_tip_media
std::string m_tip_media_key
double m_tip_media_start = 0.
int m_tip_media_frame = -2
ImVec2 m_tip_item {-1.f, -1.f}
double m_tip_hover_since = 0.
double m_tip_last_shown = -1e9
int m_tip_shown_frame = -2
int m_tip_settled = 0
ObjectID m_old_mo_id
size_t m_old_volumes_size = 0
bool m_internal_stack_active = false
bool m_schedule_update = false
Vec2d m_last_mouse_click = Vec2d::Zero()
ButtonButton m_button_down = Button::None
EState m_old_state = Off
RaycastResult m_rr = {Vec2d::Zero(), -1, Vec3f::Zero(), 0}
std::vector< CutContours > m_cut_contours
int m_volumes_index = 0
float m_cursor_z {0}
double m_height_start_z_in_imgui {0}
bool m_is_set_height_start_z_by_imgui {false}
Vec2i32 m_height_start_pos {0, 0}
bool m_is_cursor_in_imgui {false}
bool m_dirty { false }
int count = 0

Static Private Attributes

static constexpr int UV_THUMB_PX = 64
static constexpr const char * UNDOCKED_CONFIG_KEY = "texture_displacement_panel_undocked"
static std::shared_ptr< GLModel > s_sphere = nullptr

Detailed Description


Class Documentation

◆ Slic3r::GUI::GLGizmoTextureDisplacement::MixTarget

struct Slic3r::GUI::GLGizmoTextureDisplacement::MixTarget
Class Members
Vec3f lab = Vec3f::Zero()
float weight = 0.f

◆ Slic3r::GUI::GLGizmoTextureDisplacement::PreviewColorRun

struct Slic3r::GUI::GLGizmoTextureDisplacement::PreviewColorRun
Collaboration diagram for Slic3r::GUI::GLGizmoTextureDisplacement::PreviewColorRun:
Class Members
ColorRGBA color
pair< size_t, size_t > range

◆ Slic3r::GUI::GLGizmoTextureDisplacement::PartPreview

struct Slic3r::GUI::GLGizmoTextureDisplacement::PartPreview
Collaboration diagram for Slic3r::GUI::GLGizmoTextureDisplacement::PartPreview:
Class Members
vector< PreviewColorRun > color_runs
GLModel glmodel
ObjectID volume_id

◆ Slic3r::GUI::GLGizmoTextureDisplacement::LibraryTexture

struct Slic3r::GUI::GLGizmoTextureDisplacement::LibraryTexture
Collaboration diagram for Slic3r::GUI::GLGizmoTextureDisplacement::LibraryTexture:
Class Members
shared_ptr< vector< unsigned char > > image_data
unique_ptr< GLTexture > thumbnail

◆ Slic3r::GUI::GLGizmoTextureDisplacement::TipMedia

struct Slic3r::GUI::GLGizmoTextureDisplacement::TipMedia
Collaboration diagram for Slic3r::GUI::GLGizmoTextureDisplacement::TipMedia:
Class Members
wxString caption
string file

Member Typedef Documentation

◆ PaletteEntry

Member Enumeration Documentation

◆ AdjustHandle

Enumerator
None 
Pan 
AxisU 
AxisV 
1215{ None, Pan, AxisU, AxisV };
@ None
Definition SketchEngine.hpp:180

◆ Button

enum class Slic3r::GUI::GLGizmoPainterBase::Button
strongprotectedinherited
Enumerator
None 
Left 
Right 
322 {
323 None,
324 Left,
325 Right
326 };
@ Right
Definition GLToolbar.hpp:462
@ Left
Definition GLToolbar.hpp:462

◆ EGrabberExtension

enum class Slic3r::GUI::GLGizmoBase::EGrabberExtension
stronginherited
Enumerator
None 
PosX 
NegX 
PosY 
NegY 
PosZ 
NegZ 
74 {
75 None = 0,
76 PosX = 1 << 0,
77 NegX = 1 << 1,
78 PosY = 1 << 2,
79 NegY = 1 << 3,
80 PosZ = 1 << 4,
81 NegZ = 1 << 5,
82 };
@ PosX
Definition PrintConfig.hpp:350
@ NegY
Definition PrintConfig.hpp:351
@ NegX
Definition PrintConfig.hpp:351
@ PosY
Definition PrintConfig.hpp:350
@ NegZ
Definition PrintConfig.hpp:351
@ PosZ
Definition PrintConfig.hpp:350

◆ EState

Enumerator
Off 
On 
Num_States 
126 {
127 Off,
128 On,
130 };
@ On
Definition GLGizmoBase.hpp:128
@ Num_States
Definition GLGizmoBase.hpp:129
@ Off
Definition GLGizmoBase.hpp:127

◆ PrepareScope

Enumerator
PaintedParts 
AllParts 
389{ PaintedParts, AllParts };

◆ ToolType

enum class Slic3r::GUI::GLGizmoPainterBase::ToolType
strongprotectedinherited
Enumerator
BRUSH 
BUCKET_FILL 
SMART_FILL 
GAP_FILL 
274 {
275 BRUSH,
276 BUCKET_FILL,
277 SMART_FILL,
278 // BBS
279 GAP_FILL,
280 };

◆ UVBackground

Enumerator
None 
Height 
Checker 
1083{ None, Height, Checker };
@ Height
Definition Types.hpp:97

◆ UVCheckMode

Enumerator
None 
Checker 
Distortion 
1052{ None, Checker, Distortion };

Constructor & Destructor Documentation

◆ GLGizmoTextureDisplacement()

Slic3r::GUI::GLGizmoTextureDisplacement::GLGizmoTextureDisplacement ( GLCanvas3D & parent,
const std::string & icon_filename,
unsigned int sprite_id )
510 : GLGizmoPainterBase(parent, icon_filename, sprite_id)
511{
512}
GLGizmoPainterBase(GLCanvas3D &parent, const std::string &icon_filename, unsigned int sprite_id)
Definition GLGizmoPainterBase.cpp:58

References Slic3r::GUI::GLGizmoPainterBase::GLGizmoPainterBase(), and GLGizmoTextureDisplacement().

Referenced by GLGizmoTextureDisplacement(), active_layer(), any_part_painted(), shaded_preview_ready(), and texture_volume().

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

Member Function Documentation

◆ active_layer() [1/2]

TextureDisplacementLayer * Slic3r::GUI::GLGizmoTextureDisplacement::active_layer ( )
private
3502{
3503 ModelVolume *mv = texture_volume();
3504 if (mv == nullptr)
3505 return nullptr;
3506 for (TextureDisplacementLayer &l : mv->texture_displacement_layers)
3507 if (l.slot == m_active_layer_slot)
3508 return &l;
3509 return nullptr;
3510}
int m_active_layer_slot
Definition GLGizmoTextureDisplacement.hpp:748
ModelVolume * texture_volume()
Definition GLGizmoTextureDisplacement.cpp:3787

References m_active_layer_slot, Slic3r::TextureDisplacementLayer::slot, Slic3r::ModelVolume::texture_displacement_layers, and texture_volume().

Referenced by active_layer(), apply_projection_frame(), island_highlight_selection(), mark_seam_path(), on_island_edited(), on_mouse_adjust_texture(), on_render_input_window(), on_uv_vertex_edited(), push_uv_pane_state(), rebuild_seam_overlay(), rebuild_shaded_preview_mesh(), rebuild_uvcheck_mesh(), render_adjust_texture_gizmo(), render_shaded_preview_mesh(), render_uvcheck_mesh(), run_uv_command(), shaded_preview_ready(), toggle_seam_at(), update_projector(), and update_uv_editor().

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

◆ active_layer() [2/2]

const TextureDisplacementLayer * Slic3r::GUI::GLGizmoTextureDisplacement::active_layer ( ) const
private
3513{
3514 return const_cast<GLGizmoTextureDisplacement *>(this)->active_layer();
3515}
GLGizmoTextureDisplacement(GLCanvas3D &parent, const std::string &icon_filename, unsigned int sprite_id)
Definition GLGizmoTextureDisplacement.cpp:509
TextureDisplacementLayer * active_layer()
Definition GLGizmoTextureDisplacement.cpp:3501

References GLGizmoTextureDisplacement(), and active_layer().

Here is the call graph for this function:

◆ add_texture_layer()

void Slic3r::GUI::GLGizmoTextureDisplacement::add_texture_layer ( )
private
4242{
4243 ModelVolume *mv = texture_volume();
4244 if (!mv)
4245 return;
4246
4247 std::array<bool, TEXTURE_DISPLACEMENT_MAX_LAYERS> used{};
4248 for (const TextureDisplacementLayer &l : mv->texture_displacement_layers)
4249 if (l.slot >= 0 && size_t(l.slot) < used.size())
4250 used[size_t(l.slot)] = true;
4251 int free_slot = -1;
4252 for (size_t i = 0; i < used.size(); ++i)
4253 if (!used[i]) { free_slot = int(i); break; }
4254 if (free_slot < 0) {
4255 show_error(nullptr, _u8L("Maximum number of texture displacement layers reached."));
4256 return;
4257 }
4258
4259 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Add texture displacement layer"), UndoRedo::SnapshotType::GizmoAction);
4260
4261 TextureDisplacementLayer layer;
4262 layer.slot = free_slot;
4263 // A model that came with a UV channel is mapped by it. Re-unwrapping such a model throws away the
4264 // layout it was authored against and can only be worse than using it, so the default follows the
4265 // model rather than the other way round; the Mapping row still offers every other method.
4266 if (mv->has_texture_uv())
4267 layer.projection_method = TextureProjectionMethod::ModelUV;
4268 // Start the layer off on the first library texture rather than on nothing at all: a textureless
4269 // layer looks broken (painting on it appears to do nothing, because there is no height map to
4270 // displace by). The user swaps it for another from the layer's own picker.
4271 const std::vector<TextureLibraryEntry> &library = texture_library();
4272 if (!library.empty())
4273 if (const LibraryTexture *tex = get_library_texture(library.front().path)) {
4274 layer.name = library.front().name;
4275 layer.path = library.front().path;
4276 layer.image_data = tex->image_data;
4277 }
4278 mv->texture_displacement_layers.push_back(std::move(layer));
4279 // A new layer opens with every setting showing; there is nothing on it yet to hide settings from.
4280 m_layer_expanded[size_t(free_slot)] = true;
4281
4282 set_active_layer(free_slot);
4283 // set_active_layer() is a no-op when the new slot happens to be the one already active (slot 0,
4284 // for the very first layer added), so the preview would not pick the new texture up on its own.
4286 m_parent.set_as_dirty();
4287}
GLCanvas3D & m_parent
Definition GLGizmoBase.hpp:143
std::array< bool, TEXTURE_DISPLACEMENT_MAX_LAYERS > m_layer_expanded
Definition GLGizmoTextureDisplacement.hpp:520
const LibraryTexture * get_library_texture(const std::string &path)
Definition GLGizmoTextureDisplacement.cpp:4289
void rebuild_preview()
Definition GLGizmoTextureDisplacement.cpp:2378
void set_active_layer(int slot)
Definition GLGizmoTextureDisplacement.cpp:3992
Definition GLGizmoTextureDisplacement.hpp:496
static Plater * plater()
Definition GUI_Factories.cpp:400
void show_error(wxWindow *parent, const wxString &message, bool has_code_excerpts)
Definition GUI.cpp:296
const std::vector< TextureLibraryEntry > & texture_library(bool force_rescan)
Definition TextureLibrary.cpp:373
@ GizmoAction
Definition UndoRedo.hpp:36
@ ModelUV
Definition TextureDisplacement.hpp:99
#define _u8L(s)
Definition I18N.hpp:11

References _u8L, get_library_texture(), Slic3r::UndoRedo::GizmoAction, Slic3r::ModelVolume::has_texture_uv(), Slic3r::TextureDisplacementLayer::image_data, m_layer_expanded, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::ModelUV, Slic3r::TextureDisplacementLayer::name, Slic3r::TextureDisplacementLayer::path, Slic3r::GUI::plater(), Slic3r::TextureDisplacementLayer::projection_method, rebuild_preview(), set_active_layer(), Slic3r::GUI::show_error(), Slic3r::TextureDisplacementLayer::slot, Slic3r::ModelVolume::texture_displacement_layers, Slic3r::GUI::texture_library(), and texture_volume().

Referenced by update_from_model_object().

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

◆ adjust_handle_center()

Vec3f Slic3r::GUI::GLGizmoTextureDisplacement::adjust_handle_center ( const TextureDisplacementLayer & layer) const
private
3526{
3527 // apply_uv_transform() maps a planar mm coordinate p to uv = R(p / tiling_scale) + offset, and
3528 // on_mouse_adjust_texture() drives offset by offset = offset_start - R(delta / tiling_scale).
3529 // Inverting that, the handle's displacement from the anchor is - R^-1(offset) * tiling_scale -
3530 // which, substituted into the drag equation, moves the handle by exactly `delta`. So the handle
3531 // follows the cursor precisely, and is back on the anchor exactly when offset is zero.
3532 const float rad = layer.rotation_deg * float(M_PI) / 180.f;
3533 const float cs = std::cos(rad), sn = std::sin(rad);
3534 // Undo the non-square v scaling first - it is the last thing apply_uv_transform() does, so it is
3535 // the first thing to come off on the way back.
3536 const float aspect = layer_texture_aspect(layer);
3537 const Vec2f o(layer.offset.x(), (aspect > 0.f) ? layer.offset.y() / aspect : layer.offset.y());
3538 const Vec2f unrotated(o.x() * cs + o.y() * sn, -o.x() * sn + o.y() * cs);
3539 const Vec2f planar = -unrotated * layer.tiling_scale;
3540
3541 Vec3f u_axis, v_axis;
3542 adjust_tangent_basis(u_axis, v_axis);
3543 return adjust_plane_point() + u_axis * planar.x() + v_axis * planar.y();
3544}
#define M_PI
Definition ExtrusionSimulator.cpp:31
Vec3f adjust_plane_point() const
Definition GLGizmoTextureDisplacement.cpp:3517
void adjust_tangent_basis(Vec3f &u_axis, Vec3f &v_axis) const
Definition GLGizmoTextureDisplacement.cpp:3546
static float layer_texture_aspect(const TextureDisplacementLayer &layer)
Definition GLGizmoTextureDisplacement.cpp:1313
double rad(double deg)
Definition DesignSketchTool.cpp:7748
Eigen::Matrix< float, 3, 1, Eigen::DontAlign > Vec3f
Definition Point.hpp:65
Eigen::Matrix< float, 2, 1, Eigen::DontAlign > Vec2f
Definition Point.hpp:64

References adjust_plane_point(), adjust_tangent_basis(), layer_texture_aspect(), M_PI, Slic3r::TextureDisplacementLayer::offset, Slic3r::TextureDisplacementLayer::rotation_deg, and Slic3r::TextureDisplacementLayer::tiling_scale.

Referenced by on_mouse_adjust_texture(), and render_adjust_texture_gizmo().

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

◆ adjust_plane_point()

Vec3f Slic3r::GUI::GLGizmoTextureDisplacement::adjust_plane_point ( ) const
private
3518{
3519 const ModelVolume *mv = texture_volume();
3520 const float bbox_size = (mv != nullptr) ? float(mv->mesh().bounding_box().size().norm()) : 1.f;
3521 const float lift = bbox_size * 0.01f + 0.2f; // clear of the surface, to avoid z-fighting
3523}
Vec3f m_adjust_anchor_normal
Definition GLGizmoTextureDisplacement.hpp:1211
Vec3f m_adjust_anchor_pos
Definition GLGizmoTextureDisplacement.hpp:1210

References Slic3r::TriangleMesh::bounding_box(), m_adjust_anchor_normal, m_adjust_anchor_pos, Slic3r::ModelVolume::mesh(), Slic3r::BoundingBox3Base< PointType >::size(), and texture_volume().

Referenced by adjust_handle_center(), and on_mouse_adjust_texture().

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

◆ adjust_tangent_basis()

void Slic3r::GUI::GLGizmoTextureDisplacement::adjust_tangent_basis ( Vec3f & u_axis,
Vec3f & v_axis ) const
private
3547{
3548 // libslic3r's own basis, asked for rather than restated here, so the ring's "0 degrees" and the
3549 // offset handle's plane always agree with what project_texture_displacement_uv() does - including
3550 // v pointing *down* the texture and u flipping on the far side of each plane.
3552}
void box_projection_axes(const Vec3f &normal, Vec3f &u_axis, Vec3f &v_axis)
Definition TextureDisplacement.cpp:780

References Slic3r::box_projection_axes(), and m_adjust_anchor_normal.

Referenced by adjust_handle_center(), on_mouse_adjust_texture(), and render_adjust_texture_gizmo().

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

◆ any_layer_colors()

bool Slic3r::GUI::GLGizmoTextureDisplacement::any_layer_colors ( const ModelVolume & mv)
static
5701{
5702 for (const TextureDisplacementLayer &layer : mv.texture_displacement_layers)
5703 if (layer.color_enabled && !layer.empty() && height_texture_has_color(layer))
5704 return true;
5705 return false;
5706}
bool height_texture_has_color(const TextureDisplacementLayer &layer)
Definition TextureDisplacement.cpp:272

References Slic3r::TextureDisplacementLayer::color_enabled, Slic3r::TextureDisplacementLayer::empty(), Slic3r::height_texture_has_color(), and Slic3r::ModelVolume::texture_displacement_layers.

Referenced by color_settings_for(), rebuild_painted_colors(), and rebuild_subdivide_preview().

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

◆ any_part_painted()

bool Slic3r::GUI::GLGizmoTextureDisplacement::any_part_painted ( ) const
private
3825{
3826 for (const ModelVolume *mv : const_cast<GLGizmoTextureDisplacement *>(this)->texture_parts())
3827 if (mv->is_texture_displacement_painted())
3828 return true;
3829 return false;
3830}
std::vector< ModelVolume * > texture_parts()
Definition GLGizmoTextureDisplacement.cpp:3803

References GLGizmoTextureDisplacement(), and texture_parts().

Referenced by bake_standard(), rebuild_preview(), and subdivide_model_adaptive().

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

◆ apply_clipping_plane()

virtual bool Slic3r::GUI::GLGizmoBase::apply_clipping_plane ( )
inlinevirtualinherited
195{ return true; }

Referenced by Slic3r::GUI::GLCanvas3D::_render_objects(), and Slic3r::GUI::GLCanvas3D::get_raycaster_clipping_plane().

Here is the caller graph for this function:

◆ apply_projection_frame()

int Slic3r::GUI::GLGizmoTextureDisplacement::apply_projection_frame ( )
private
3225{
3226 TextureDisplacementLayer *layer = active_layer();
3227 const ModelVolume *mv = texture_volume();
3228 const ModelObject *mo = m_c->selection_info()->model_object();
3229 wxGLCanvas *cnv = m_parent.get_wxglcanvas();
3230 if (layer == nullptr || mv == nullptr || mo == nullptr || cnv == nullptr || m_projector_frame == nullptr ||
3231 !m_projector_frame->IsShown())
3232 return -1;
3233
3234 // The frame's gate, brought from screen coordinates into the GL viewport's pixel space. Two
3235 // conversions, both necessary: ScreenToClient() because the viewport's origin is the canvas's
3236 // top-left, not the desktop's, and the retina scale because the viewport is sized in physical
3237 // pixels (see GLCanvas3D::get_canvas_size()) while wx hands out logical ones.
3238 const wxRect gate = m_projector_frame->client_rect_on_screen();
3239 const wxPoint tl = cnv->ScreenToClient(gate.GetTopLeft());
3240 const double scale = double(m_parent.get_scale());
3241 const double rx = double(tl.x) * scale, ry = double(tl.y) * scale;
3242 const double rw = double(gate.width) * scale, rh = double(gate.height) * scale;
3243 if (rw < 1.0 || rh < 1.0)
3244 return -1;
3245
3246 const Camera &camera = wxGetApp().plater()->get_camera();
3247 const std::array<int, 4> &vp = camera.get_viewport();
3248 const Selection &sel = m_parent.get_selection();
3249 const Transform3d trafo = mo->instances[sel.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
3250
3251 // Local space -> clip space, the same product the renderer uses, so the mapping agrees with what
3252 // is actually on screen rather than with an idealisation of it.
3253 const Eigen::Matrix4d K = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix() * trafo.matrix();
3254
3255 // Window coordinates follow igl::project's convention (as CameraUtils::project does):
3256 // win_x = vp.x + vp.w * (ndc.x + 1) / 2, and y measured downward as vp.h - win_y_gl.
3257 // Turning those into uv = (win - rect_origin) / rect_size gives u and v as affine functions of
3258 // ndc.x and ndc.y, and since ndc = clip.xyz / clip.w, multiplying through by clip.w leaves a
3259 // plain linear combination of K's rows - i.e. one 3x4 matrix carrying the perspective divide.
3260 const double A = double(vp[2]) / (2.0 * rw);
3261 const double B = (double(vp[0]) + double(vp[2]) * 0.5 - rx) / rw;
3262 const double C = -double(vp[3]) / (2.0 * rh);
3263 const double D = (double(vp[3]) * 0.5 - double(vp[1]) - ry) / rh;
3264
3265 const Eigen::Vector4d row_u = A * K.row(0).transpose() + B * K.row(3).transpose();
3266 const Eigen::Vector4d row_v = C * K.row(1).transpose() + D * K.row(3).transpose();
3267 const Eigen::Vector4d row_w = K.row(3).transpose();
3268
3269 std::array<float, 12> m{};
3270 for (int i = 0; i < 4; ++i) {
3271 m[size_t(i)] = float(row_u[i]);
3272 m[size_t(4 + i)] = float(row_v[i]);
3273 m[size_t(8 + i)] = float(row_w[i]);
3274 }
3275
3276 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Apply texture projection frame"),
3278
3279 layer->projection_method = TextureProjectionMethod::ViewProjected;
3280 layer->view_project_projective = true;
3281 layer->view_project_matrix = m;
3282 // Off, so the height sampler returns 0 outside [0,1) and the frame's border becomes a hard edge
3283 // of the displacement rather than the first seam of an endless repeat.
3284 layer->tile_enabled = false;
3285 // The affine axes are kept up to date too, so turning the projective mapping off later leaves a
3286 // sane flat projection from this same viewpoint instead of whatever was captured long ago.
3288
3289 const int painted = select_visible_faces(&m);
3292 m_parent.set_as_dirty();
3293 return painted;
3294}
int scale(const int val)
Definition WipeTowerDialog.cpp:58
CommonGizmosDataPool * m_c
Definition GLGizmoBase.hpp:155
void capture_view_projection(TextureDisplacementLayer &layer)
Definition GLGizmoTextureDisplacement.cpp:3141
bool m_preview_params_dirty
Definition GLGizmoTextureDisplacement.hpp:907
int select_visible_faces(const std::array< float, 12 > *uv_clip=nullptr)
Definition GLGizmoTextureDisplacement.cpp:5252
TextureProjectorFrame * m_projector_frame
Definition GLGizmoTextureDisplacement.hpp:451
@ Selection
Definition Shortcuts.hpp:72
@ Camera
Definition Shortcuts.hpp:72
@ K
Definition CalibrationWizardSavePage.cpp:123
Eigen::Transform< double, 3, Eigen::Affine, Eigen::DontAlign > Transform3d
Definition Point.hpp:96
@ ViewProjected
Definition TextureDisplacement.hpp:88

References _u8L, active_layer(), capture_view_projection(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::Camera::get_view_matrix(), Slic3r::GUI::Camera::get_viewport(), Slic3r::UndoRedo::GizmoAction, Slic3r::ModelObject::instances, Slic3r::GUI::K, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, m_preview_params_dirty, m_projector_frame, Slic3r::GUI::plater(), Slic3r::TextureDisplacementLayer::projection_method, rebuild_preview(), scale(), select_visible_faces(), texture_volume(), Slic3r::TextureDisplacementLayer::tile_enabled, Slic3r::TextureDisplacementLayer::view_project_matrix, Slic3r::TextureDisplacementLayer::view_project_projective, and Slic3r::ViewProjected.

Here is the call graph for this function:

◆ apply_standard_mode_presets()

bool Slic3r::GUI::GLGizmoTextureDisplacement::apply_standard_mode_presets ( ModelVolume * mv)
private
6670{
6671 bool changed = false;
6672 const auto pin = [&changed](auto &field, auto value) {
6673 if (field != value) {
6674 field = value;
6675 changed = true;
6676 }
6677 };
6678
6679 if (mv != nullptr) {
6680 pin(mv->texture_displacement_options.displace_border, true);
6681 pin(mv->texture_displacement_options.smooth_enabled, false);
6682 }
6683 pin(m_subdivide_adaptive, true);
6684 pin(m_subdivide_feature, true);
6690 // Deliberately *not* pinned: the triangle budget stays visible and editable in Standard mode, so
6691 // pinning it would fight the user's own slider every frame.
6693 pin(m_remesh_keep_sharp_edges, true);
6695 return changed;
6696}
bool m_remesh_keep_sharp_edges
Definition GLGizmoTextureDisplacement.hpp:851
float m_subdivide_target_mm
Definition GLGizmoTextureDisplacement.hpp:789
float m_subdivide_min_edge_mm
Definition GLGizmoTextureDisplacement.hpp:797
float m_remesh_target_edge_mm
Definition GLGizmoTextureDisplacement.hpp:847
float m_remesh_sharp_angle_deg
Definition GLGizmoTextureDisplacement.hpp:850
bool m_subdivide_feature
Definition GLGizmoTextureDisplacement.hpp:795
bool m_subdivide_adaptive
Definition GLGizmoTextureDisplacement.hpp:788
float m_subdivide_detail_mm
Definition GLGizmoTextureDisplacement.hpp:796
float m_subdivide_color_mm
Definition GLGizmoTextureDisplacement.hpp:812
float m_subdivide_border_mm
Definition GLGizmoTextureDisplacement.hpp:805
static constexpr float STD_SUBDIV_DETAIL_MM
Definition GLGizmoTextureDisplacement.cpp:6655
static constexpr float STD_SUBDIV_MIN_EDGE_MM
Definition GLGizmoTextureDisplacement.cpp:6659
static constexpr float STD_REMESH_EDGE_MM
Definition GLGizmoTextureDisplacement.cpp:6652
static constexpr float STD_SUBDIV_MAX_EDGE_MM
Definition GLGizmoTextureDisplacement.cpp:6654
static constexpr float STD_SUBDIV_BORDER_MM
Definition GLGizmoTextureDisplacement.cpp:6663
static constexpr float STD_REMESH_SHARP_DEG
Definition GLGizmoTextureDisplacement.cpp:6653
static constexpr float STD_SUBDIV_COLOR_MM
Definition GLGizmoTextureDisplacement.cpp:6667

References Slic3r::TextureDisplacementOptions::displace_border, m_remesh_keep_sharp_edges, m_remesh_sharp_angle_deg, m_remesh_target_edge_mm, m_subdivide_adaptive, m_subdivide_border_mm, m_subdivide_color_mm, m_subdivide_detail_mm, m_subdivide_feature, m_subdivide_min_edge_mm, m_subdivide_target_mm, Slic3r::TextureDisplacementOptions::smooth_enabled, Slic3r::GUI::STD_REMESH_EDGE_MM, Slic3r::GUI::STD_REMESH_SHARP_DEG, Slic3r::GUI::STD_SUBDIV_BORDER_MM, Slic3r::GUI::STD_SUBDIV_COLOR_MM, Slic3r::GUI::STD_SUBDIV_DETAIL_MM, Slic3r::GUI::STD_SUBDIV_MAX_EDGE_MM, Slic3r::GUI::STD_SUBDIV_MIN_EDGE_MM, and Slic3r::ModelVolume::texture_displacement_options.

Referenced by bake_standard(), on_render_input_window(), and run_stage_debug().

Here is the caller graph for this function:

◆ apply_view_mode()

void Slic3r::GUI::GLGizmoTextureDisplacement::apply_view_mode ( int mode)
private
2846{
2847 m_use_shaded_preview = (mode == 1);
2848 m_uv_check_mode = (mode == 2) ? UVCheckMode::Checker :
2849 (mode == 3) ? UVCheckMode::Distortion : UVCheckMode::None;
2853 else
2854 // The Fast view skips the CPU displacement entirely (see rebuild_preview()), so leaving it means
2855 // m_preview_glmodel may be stale or absent - ask for it now.
2857 // ...and the paint tint between the base and the displaced surface, for the same reason.
2858 m_paint_overlay_dirty = true;
2859 refresh_wireframe(); // Normal<->Fast swaps the wireframe between displaced and base mesh
2860 update_uv_editor(); // mirror the checker / distortion heatmap into the UV pane too
2861 m_parent.set_as_dirty();
2862}
void rebuild_uvcheck_mesh()
Definition GLGizmoTextureDisplacement.cpp:2146
UVCheckMode m_uv_check_mode
Definition GLGizmoTextureDisplacement.hpp:1053
void update_uv_editor()
Definition GLGizmoTextureDisplacement.cpp:2581
void refresh_wireframe()
Definition GLGizmoTextureDisplacement.cpp:2335
bool m_use_shaded_preview
Definition GLGizmoTextureDisplacement.hpp:913
void queue_preview_job()
Definition GLGizmoTextureDisplacement.cpp:2454
bool m_paint_overlay_dirty
Definition GLGizmoTextureDisplacement.hpp:934
void rebuild_shaded_preview_mesh()
Definition GLGizmoTextureDisplacement.cpp:1448
UVCheckMode
Definition GLGizmoTextureDisplacement.hpp:1052
@ Checker
Definition GLGizmoTextureDisplacement.hpp:1052
@ None
Definition GLGizmoTextureDisplacement.hpp:1052
@ Distortion
Definition GLGizmoTextureDisplacement.hpp:1052

References Checker, Distortion, m_paint_overlay_dirty, Slic3r::GUI::GLGizmoBase::m_parent, m_use_shaded_preview, m_uv_check_mode, None, queue_preview_job(), rebuild_shaded_preview_mesh(), rebuild_uvcheck_mesh(), refresh_wireframe(), and update_uv_editor().

Referenced by follow_island_highlight(), and run_uv_command().

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

◆ bake()

void Slic3r::GUI::GLGizmoTextureDisplacement::bake ( bool own_snapshot = true)
private
6604{
6605 ModelVolume *owner = texture_volume();
6606 if (owner == nullptr || m_bake_in_progress)
6607 return;
6608
6609 // Make sure the currently active layer's in-progress edits are flushed into the model before
6610 // baking, otherwise the most recent, not-yet-committed strokes would be silently skipped.
6612
6613 // Every painted part, not only the one holding the layer stack: the brush paints across all of
6614 // them (see update_model_object()), so baking one would leave the user's paint on the others
6615 // untouched and their geometry flat.
6616 const std::vector<ModelVolume *> parts = painted_parts();
6617 if (parts.empty()) {
6618 show_error(nullptr, _u8L("Nothing is painted, there is nothing to bake."));
6619 return;
6620 }
6621
6622 const Vec3d anchor = texture_anchor();
6623 const std::vector<float> drops = resting_drops(parts);
6624 std::vector<TextureDisplacementBakeVolume> volumes;
6625 volumes.reserve(parts.size());
6626 for (size_t i = 0; i < parts.size(); ++i)
6627 volumes.push_back(texture_displacement_bake_volume(*parts[i], layers_for_part(*parts[i]),
6628 owner->texture_displacement_options, anchor, drops[i]));
6629
6630 m_bake_in_progress = true;
6631 queue_texture_displacement_bake(std::move(volumes), color_settings_for(*owner), [this]() {
6632 m_bake_in_progress = false;
6633 // Baking replaces each baked part's mesh (new id, new topology) without changing the object's
6634 // id or volume count, so GLGizmoPainterBase::data_changed()'s usual change-detection never
6635 // notices it needs to reload - do it explicitly here, otherwise the gizmo keeps painting
6636 // and rendering against the stale, pre-bake TriangleSelectorPatch until the object is
6637 // deselected and reselected.
6638 if (m_state == On && m_c->selection_info() && m_c->selection_info()->model_object())
6640 m_parent.set_as_dirty();
6641 }, own_snapshot);
6642}
EState m_state
Definition GLGizmoBase.hpp:146
void update_from_model_object(bool first_update) override
Definition GLGizmoTextureDisplacement.cpp:3913
std::vector< float > resting_drops(const std::vector< ModelVolume * > &parts)
Definition GLGizmoTextureDisplacement.cpp:3855
std::vector< ModelVolume * > painted_parts()
Definition GLGizmoTextureDisplacement.cpp:3815
void update_model_object() override
Definition GLGizmoTextureDisplacement.cpp:3879
Vec3d texture_anchor() const
Definition GLGizmoTextureDisplacement.cpp:3849
std::vector< TextureDisplacementLayer > layers_for_part(const ModelVolume &part) const
Definition GLGizmoTextureDisplacement.cpp:3832
TextureColorSettings color_settings_for(const ModelVolume &mv)
Definition GLGizmoTextureDisplacement.cpp:5708
bool m_bake_in_progress
Definition GLGizmoTextureDisplacement.hpp:760
void queue_texture_displacement_bake(std::vector< TextureDisplacementBakeVolume > volumes, const TextureColorSettings &color, std::function< void()> on_finished, bool take_snapshot)
Definition TextureDisplacementBakeJob.cpp:359
TextureDisplacementBakeVolume texture_displacement_bake_volume(const ModelVolume &volume, const std::vector< TextureDisplacementLayer > &layers, const TextureDisplacementOptions &options, const Vec3d &texture_anchor, float resting_drop)
Definition TextureDisplacementBakeJob.cpp:338
Eigen::Matrix< double, 3, 1, Eigen::DontAlign > Vec3d
Definition Point.hpp:68

References _u8L, color_settings_for(), layers_for_part(), m_bake_in_progress, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::GUI::GLGizmoBase::m_state, Slic3r::GUI::GLGizmoBase::On, painted_parts(), Slic3r::GUI::queue_texture_displacement_bake(), resting_drops(), Slic3r::GUI::show_error(), texture_anchor(), Slic3r::GUI::texture_displacement_bake_volume(), Slic3r::ModelVolume::texture_displacement_options, texture_volume(), update_from_model_object(), and update_model_object().

Referenced by bake_standard(), and queue_prepare().

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

◆ bake_standard()

void Slic3r::GUI::GLGizmoTextureDisplacement::bake_standard ( )
private
6699{
6700 ModelVolume *mv = texture_volume();
6701 if (mv == nullptr || m_bake_in_progress || m_prepare_in_progress)
6702 return;
6703
6704 update_model_object(); // flush the active layer's in-progress strokes before anything reads them
6705 if (!any_part_painted()) {
6706 show_error(nullptr, _u8L("Nothing is painted, there is nothing to bake."));
6707 return;
6708 }
6709 apply_standard_mode_presets(mv); // belt and braces: never bake with values the panel is not showing
6710
6711 // It refines as part of the bake, so preparing first would refine a second time at another
6712 // target.
6713 if (mv->texture_displacement_options.pipeline_v2) {
6714 bake();
6715 return;
6716 }
6717
6718 // The whole recipe in one go. Either stage having nothing to do is normal, not a failure - a mesh
6719 // that is already even needs no remesh, one that is already fine enough for the texture needs no
6720 // subdivision - so no "nothing changed" message here: it goes straight on to the displacement.
6721 TextureDisplacementPrepareParams params;
6722 params.remesh_edge_mm = STD_REMESH_EDGE_MM;
6723 params.remesh_sharp_deg = STD_REMESH_SHARP_DEG;
6724 params.subdiv_target_mm = STD_SUBDIV_MAX_EDGE_MM;
6725 params.subdiv_detail_mm = STD_SUBDIV_DETAIL_MM;
6726 params.subdiv_min_edge_mm = STD_SUBDIV_MIN_EDGE_MM;
6727 params.subdiv_border_mm = STD_SUBDIV_BORDER_MM;
6728 params.subdiv_color_edge_mm = STD_SUBDIV_COLOR_MM;
6729 params.subdiv_feature = true;
6730 params.subdiv_added_triangles = m_subdivide_budget_k * 1000;
6731 params.cut_steps = true;
6732 queue_prepare(params, _u8L("Bake texture displacement"), /* then_bake */ true, {});
6733}
bool apply_standard_mode_presets(ModelVolume *mv)
Definition GLGizmoTextureDisplacement.cpp:6669
int m_subdivide_budget_k
Definition GLGizmoTextureDisplacement.hpp:824
void bake(bool own_snapshot=true)
Definition GLGizmoTextureDisplacement.cpp:6603
bool any_part_painted() const
Definition GLGizmoTextureDisplacement.cpp:3824
void queue_prepare(const TextureDisplacementPrepareParams &params, const std::string &snapshot_name, bool then_bake, const std::string &unchanged_msg, PrepareScope scope=PrepareScope::PaintedParts)
Definition GLGizmoTextureDisplacement.cpp:6928
bool m_prepare_in_progress
Definition GLGizmoTextureDisplacement.hpp:396

References _u8L, any_part_painted(), apply_standard_mode_presets(), bake(), Slic3r::TextureDisplacementPrepareParams::cut_steps, m_bake_in_progress, m_prepare_in_progress, m_subdivide_budget_k, Slic3r::TextureDisplacementOptions::pipeline_v2, queue_prepare(), Slic3r::TextureDisplacementPrepareParams::remesh_edge_mm, Slic3r::TextureDisplacementPrepareParams::remesh_sharp_deg, Slic3r::GUI::show_error(), Slic3r::GUI::STD_REMESH_EDGE_MM, Slic3r::GUI::STD_REMESH_SHARP_DEG, Slic3r::GUI::STD_SUBDIV_BORDER_MM, Slic3r::GUI::STD_SUBDIV_COLOR_MM, Slic3r::GUI::STD_SUBDIV_DETAIL_MM, Slic3r::GUI::STD_SUBDIV_MAX_EDGE_MM, Slic3r::GUI::STD_SUBDIV_MIN_EDGE_MM, Slic3r::TextureDisplacementPrepareParams::subdiv_added_triangles, Slic3r::TextureDisplacementPrepareParams::subdiv_border_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_color_edge_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_detail_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_feature, Slic3r::TextureDisplacementPrepareParams::subdiv_min_edge_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_target_mm, Slic3r::ModelVolume::texture_displacement_options, texture_volume(), and update_model_object().

Here is the call graph for this function:

◆ bounding_box()

BoundingBoxf3 Slic3r::GUI::GLGizmoPainterBase::bounding_box ( ) const
privateinherited
478{
479 BoundingBoxf3 ret;
480 const Selection& selection = m_parent.get_selection();
481 const Selection::IndicesList& idxs = selection.get_volume_idxs();
482 for (unsigned int i : idxs) {
483 const GLVolume* volume = selection.get_volume(i);
484 if (!volume->is_modifier)
485 ret.merge(volume->transformed_convex_hull_bounding_box());
486 }
487 return ret;
488}
std::set< unsigned int > IndicesList
Definition Selection.hpp:49

References Slic3r::GUI::Selection::get_volume(), Slic3r::GUI::Selection::get_volume_idxs(), Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::BoundingBox3Base< PointType >::merge(), and Slic3r::GLVolume::transformed_convex_hull_bounding_box().

Referenced by render_cursor_height_range().

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

◆ build_island_move_set()

std::vector< int > Slic3r::GUI::GLGizmoTextureDisplacement::build_island_move_set ( const TextureDisplacementLayer & layer,
int primary ) const
private
1616{
1617 std::vector<int> set;
1618 const int chart_count = std::max(m_uv_editor_unwrap.chart_count, 0);
1619
1620 // Seed with the pane's multi-selection (falling back to just the primary if the canvas has none).
1621 std::vector<int> seeds;
1622 if (const UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas())
1623 seeds = canvas->selected_islands();
1624 if (seeds.empty() && primary >= 0)
1625 seeds.push_back(primary);
1626
1627 const auto add = [&set](int c) {
1628 if (c >= 0 && std::find(set.begin(), set.end(), c) == set.end())
1629 set.push_back(c);
1630 };
1631 for (int s : seeds) {
1632 add(s);
1633 // Pull in every chart sharing s's join group, so a joined pair moves as one.
1634 const int gs = island_group_of(layer.island_groups, s);
1635 for (int c = 0; c < chart_count; ++c)
1636 if (island_group_of(layer.island_groups, c) == gs)
1637 add(c);
1638 }
1639 add(primary); // never leave the primary out, whatever the selection state
1640 return set;
1641}
static int island_group_of(const std::vector< int > &groups, int c)
Definition GLGizmoTextureDisplacement.cpp:1586
PatchUnwrap m_uv_editor_unwrap
Definition GLGizmoTextureDisplacement.hpp:1180
SpiralVase::SpiralPoint add(SpiralVase::SpiralPoint a, SpiralVase::SpiralPoint b)
Definition SpiralVase.cpp:26
void set(id object, const void *key, id value)
Definition MacIME.mm:24

References island_group_of(), Slic3r::TextureDisplacementLayer::island_groups, m_uv_editor_unwrap, and Slic3r::GUI::plater().

Referenced by on_island_edited().

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

◆ build_wireframe_from_its()

void Slic3r::GUI::GLGizmoTextureDisplacement::build_wireframe_from_its ( const indexed_triangle_set & its)
private
2292{
2294 m_wireframe_overlay_vcount = its.vertices.size();
2295 if (its.indices.empty())
2296 return;
2297
2298 GLModel::Geometry init_data;
2300 init_data.reserve_vertices(its.vertices.size());
2301 init_data.reserve_indices(its.indices.size() * 6);
2302 for (const Vec3f &v : its.vertices)
2303 init_data.add_vertex(v);
2304 // One segment per triangle edge; shared edges drawn twice, harmless for a wireframe and far
2305 // cheaper than deduplicating a million of them.
2306 for (const stl_triangle_vertex_indices &tri : its.indices)
2307 for (int i = 0; i < 3; ++i)
2308 init_data.add_line(unsigned(tri[i]), unsigned(tri[(i + 1) % 3]));
2309
2310 if (!init_data.is_empty())
2311 m_wireframe_overlay_glmodel.init_from(std::move(init_data));
2312}
GLModel m_wireframe_overlay_glmodel
Definition GLGizmoTextureDisplacement.hpp:1096
size_t m_wireframe_overlay_vcount
Definition GLGizmoTextureDisplacement.hpp:1097

References Slic3r::GUI::GLModel::Geometry::add_line(), Slic3r::GUI::GLModel::Geometry::add_vertex(), Slic3r::GUI::GLModel::Geometry::format, Slic3r::GUI::GLModel::Geometry::is_empty(), Slic3r::GUI::GLModel::Geometry::Lines, m_wireframe_overlay_glmodel, m_wireframe_overlay_vcount, Slic3r::GUI::GLModel::Geometry::P3, Slic3r::GUI::GLModel::Geometry::reserve_indices(), and Slic3r::GUI::GLModel::Geometry::reserve_vertices().

Referenced by rebuild_wireframe_overlay(), and refresh_wireframe().

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

◆ cached_palette()

const std::vector< GLGizmoTextureDisplacement::PaletteEntry > & Slic3r::GUI::GLGizmoTextureDisplacement::cached_palette ( )
5720{
5721 // Rebuilt only when something it depends on actually changes. The shaded preview rebuilds on every
5722 // paint stroke and the panel asks every frame, while ranking the mixes and filling the quantizer's
5723 // lookup cube each take tens of milliseconds - paying that per stroke is the difference between
5724 // painting that keeps up and painting that stutters.
5725 //
5726 // Two levels. The ranking depends only on the filaments and the images, so dragging the count, or a
5727 // bake creating slots, re-picks from it without ranking again; the palette and its quantizers depend
5728 // on that pick as well.
5729 const ModelVolume *mv = texture_volume();
5730 const bool mixing = mv != nullptr && mv->texture_displacement_options.color_mix_enabled;
5731 const int mix_count = mv != nullptr ? std::max(0, mv->texture_displacement_options.color_mix_count) : 0;
5732 std::vector<ColorRGBA> filaments = filament_palette();
5733 // The images themselves rather than their addresses, so a freed and reallocated image can never
5734 // pass for the old one. Whether one has colour, and whether its colours are flat, follows from it.
5735 std::vector<std::shared_ptr<std::vector<unsigned char>>> images;
5736 if (mv != nullptr)
5737 for (const TextureDisplacementLayer &layer : mv->texture_displacement_layers)
5738 if (layer.color_enabled && !layer.empty())
5739 images.push_back(layer.image_data);
5740
5741 const bool ranking_stale = filaments != m_palette_filaments || images != m_palette_images;
5742 if (ranking_stale) {
5743 m_palette_filaments = std::move(filaments);
5744 m_palette_images = std::move(images);
5745 m_mix_ranking.reset();
5746 }
5747 if (mixing && !m_mix_ranking)
5748 m_mix_ranking = rank_mixes(m_palette_filaments, mix_targets(mv->texture_displacement_layers),
5750
5751 // Which mixes the project can still print: those it has a fixed slot for, plus as many new ones as
5752 // there are free slots.
5753 const PresetBundle &bundle = *wxGetApp().preset_bundle;
5754 const int free_slots = std::max(0, int(EnforcerBlockerType::ExtruderMax) - int(bundle.filament_presets.size()));
5755 const std::vector<std::string> slots = mixed_slot_signature(bundle.project_config);
5756 if (ranking_stale || m_palette_cache.empty() || mixing != m_palette_mixing || mix_count != m_palette_mix_count ||
5757 free_slots != m_palette_free_slots || slots != m_palette_slots) {
5758 m_palette_mixing = mixing;
5759 m_palette_mix_count = mix_count;
5760 m_palette_free_slots = free_slots;
5761 m_palette_slots = slots;
5762 std::vector<PaletteEntry> mixes;
5763 if (mixing && m_mix_ranking)
5764 mixes = pick_mixes(*m_mix_ranking, mix_count, free_slots, [&bundle](const PaletteEntry &e) {
5765 return find_fixed_mixed_filament(bundle.project_config, {unsigned(e.a + 1), unsigned(e.b + 1)},
5766 {e.a_percent(), 100 - e.a_percent()}) >= 0;
5767 });
5768 std::vector<PaletteEntry> palette = make_palette(m_palette_filaments, mixes);
5769 // The previews keep what they were drawn with, so a palette that really changed under them -
5770 // a filament or a mixed slot edited in the sidebar - has to send them round again.
5772 m_palette_cache = std::move(palette);
5773 m_palette_quantizer = nullptr;
5774 m_palette_pure_quantizer = nullptr;
5775 }
5776 return m_palette_cache;
5777}
PrintableColor PaletteEntry
Definition GLGizmoTextureDisplacement.hpp:92
static std::vector< PaletteEntry > pick_mixes(const std::vector< PaletteEntry > &ranking, int count, int free_slots, const std::function< bool(const PaletteEntry &)> &reusable)
Definition GLGizmoTextureDisplacement.cpp:5966
int m_palette_free_slots
Definition GLGizmoTextureDisplacement.hpp:177
std::vector< ColorRGBA > m_palette_filaments
Definition GLGizmoTextureDisplacement.hpp:172
std::vector< PaletteEntry > m_palette_cache
Definition GLGizmoTextureDisplacement.hpp:166
std::optional< std::vector< PaletteEntry > > m_mix_ranking
Definition GLGizmoTextureDisplacement.hpp:174
std::vector< std::string > m_palette_slots
Definition GLGizmoTextureDisplacement.hpp:178
int m_palette_mix_count
Definition GLGizmoTextureDisplacement.hpp:176
bool m_palette_changed
Definition GLGizmoTextureDisplacement.hpp:170
static std::vector< ColorRGBA > filament_palette()
Definition GLGizmoTextureDisplacement.cpp:5790
std::vector< std::shared_ptr< std::vector< unsigned char > > > m_palette_images
Definition GLGizmoTextureDisplacement.hpp:173
static std::vector< PaletteEntry > make_palette(const std::vector< ColorRGBA > &filaments, const std::vector< PaletteEntry > &mixes)
Definition GLGizmoTextureDisplacement.cpp:5984
bool m_palette_mixing
Definition GLGizmoTextureDisplacement.hpp:175
static std::vector< MixTarget > mix_targets(const std::vector< TextureDisplacementLayer > &layers)
Definition GLGizmoTextureDisplacement.cpp:5811
static std::vector< PaletteEntry > rank_mixes(const std::vector< ColorRGBA > &filaments, const std::vector< MixTarget > &targets, int limit)
Definition GLGizmoTextureDisplacement.cpp:5883
ColorQuantizeFn m_palette_quantizer
Definition GLGizmoTextureDisplacement.hpp:167
ColorQuantizeFn m_palette_pure_quantizer
Definition GLGizmoTextureDisplacement.hpp:168
std::vector< std::string > mixed_slot_signature(const DynamicPrintConfig &project_config)
Definition GLGizmoTextureDisplacement.cpp:346
bool same_palette(const std::vector< PrintableColor > &l, const std::vector< PrintableColor > &r)
Definition GLGizmoTextureDisplacement.cpp:364
int find_fixed_mixed_filament(const ConfigBase &project_config, const std::vector< unsigned int > &components, const std::vector< int > &weights)
Definition FilamentMixer.cpp:461
@ ExtruderMax
Definition TriangleSelector.hpp:66

References Slic3r::TextureDisplacementLayer::color_enabled, Slic3r::TextureDisplacementOptions::color_mix_count, Slic3r::TextureDisplacementOptions::color_mix_enabled, Slic3r::TextureDisplacementLayer::empty(), Slic3r::ExtruderMax, filament_palette(), Slic3r::PresetBundle::filament_presets, Slic3r::find_fixed_mixed_filament(), Slic3r::TextureDisplacementLayer::image_data, m_mix_ranking, m_palette_cache, m_palette_changed, m_palette_filaments, m_palette_free_slots, m_palette_images, m_palette_mix_count, m_palette_mixing, m_palette_pure_quantizer, m_palette_quantizer, m_palette_slots, make_palette(), mix_targets(), pick_mixes(), Slic3r::PresetBundle::project_config, rank_mixes(), Slic3r::ModelVolume::texture_displacement_layers, Slic3r::ModelVolume::texture_displacement_options, and texture_volume().

Referenced by color_settings_for(), on_render_input_window(), palette_quantizers(), and rebuild_shaded_preview_mesh().

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

◆ capture_view_projection()

void Slic3r::GUI::GLGizmoTextureDisplacement::capture_view_projection ( TextureDisplacementLayer & layer)
private
3142{
3143 const ModelVolume *mv = texture_volume();
3144 const ModelObject *mo = m_c->selection_info()->model_object();
3145 if (mv == nullptr || mo == nullptr)
3146 return;
3147
3148 const Camera &camera = wxGetApp().plater()->get_camera();
3149 const Selection &selection = m_parent.get_selection();
3150 const Transform3d trafo = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
3151
3152 // The view matrix's rotation rows are the camera axes in world space; bring them into the
3153 // volume's local frame (where the mesh vertices live) so the projector rides along with the part.
3154 const Matrix3d view_rot = camera.get_view_matrix().matrix().block<3, 3>(0, 0);
3155 const Matrix3d trafo_rot = trafo.matrix().block<3, 3>(0, 0);
3156 const Matrix3d world_to_local = trafo_rot.inverse();
3157 const Vec3d local_right = world_to_local * Vec3d(view_rot.row(0).transpose());
3158 const Vec3d local_up = world_to_local * Vec3d(view_rot.row(1).transpose());
3159
3160 // Unit axes: the projected planar coordinate must stay in mm so tiling_scale keeps meaning mm.
3161 layer.view_project_right = local_right.norm() > 1e-9 ? Vec3f(local_right.normalized().cast<float>()) : Vec3f::UnitX();
3162 layer.view_project_up = local_up.norm() > 1e-9 ? Vec3f(local_up.normalized().cast<float>()) : Vec3f::UnitY();
3163}
Eigen::Matrix< double, 3, 3, Eigen::DontAlign > Matrix3d
Definition Point.hpp:86

References Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_view_matrix(), Slic3r::ModelObject::instances, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, texture_volume(), Slic3r::TextureDisplacementLayer::view_project_right, and Slic3r::TextureDisplacementLayer::view_project_up.

Referenced by apply_projection_frame().

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

◆ close_image_adjust()

void Slic3r::GUI::GLGizmoTextureDisplacement::close_image_adjust ( )
private
4873{
4874 // Revert the layer's live adjustment. Every exit that reaches here is a discard: a commit resets
4875 // preview_adjust itself, before pointing the layer at the file that already carries the edit, so this
4876 // cannot undo a committed adjustment.
4877 //
4878 // Found by the id recorded when the panel opened rather than through texture_volume(), which answers
4879 // for the *current* selection. One of the ways to get here is the selection having moved on, and
4880 // reverting the newly selected object's layer instead would both miss the adjustment that is still
4881 // live on the original volume and clear one the user never touched.
4882 bool reverted = false;
4883 if (m_adjust_slot >= 0 && m_adjust_volume_id != 0)
4884 for (ModelObject *mo : wxGetApp().model().objects) {
4885 for (ModelVolume *mv : mo->volumes) {
4886 if (mv->id().id != m_adjust_volume_id)
4887 continue;
4888 for (TextureDisplacementLayer &l : mv->texture_displacement_layers)
4889 if (l.slot == m_adjust_slot && !l.preview_adjust.identity()) {
4890 l.preview_adjust = TextureAdjustments{};
4891 reverted = true;
4892 }
4893 }
4894 }
4895
4896 if (reverted)
4898
4899 // std::async's future blocks here until the pass finishes. That is a few milliseconds on an image
4900 // this small, and the alternative - a detached thread writing into the members of a closed panel -
4901 // is worse.
4902 if (m_adjust_preview_job.valid())
4903 m_adjust_preview_job.wait();
4905
4906 m_adjust_slot = -1;
4907 m_adjust = TextureAdjustments{};
4908 m_adjust_applied = TextureAdjustments{};
4909 m_adjust_source = DecodedHeightTexture{};
4910 m_adjust_preview_source = DecodedHeightTexture{};
4911 m_adjust_source_image = nullptr;
4913 m_adjust_preview_tex.reset();
4914 m_adjust_preview_valid = false;
4915
4916 // Only while the gizmo is up: on_shutdown() also comes through here, and rebuilding a preview for a
4917 // gizmo that is closing would be work for a model nobody is looking at. The reverted layer data is
4918 // what matters there, and that is already done above.
4919 if (reverted && m_state == On) {
4921 m_parent.set_as_dirty();
4922 }
4923}
std::unique_ptr< GLTexture > m_adjust_preview_tex
Definition GLGizmoTextureDisplacement.hpp:578
size_t m_adjust_volume_id
Definition GLGizmoTextureDisplacement.hpp:576
std::future< DecodedHeightTexture > m_adjust_preview_job
Definition GLGizmoTextureDisplacement.hpp:585
DecodedHeightTexture m_adjust_preview_source
Definition GLGizmoTextureDisplacement.hpp:569
void invalidate_adjust_textures()
Definition GLGizmoTextureDisplacement.cpp:4859
TextureAdjustments m_adjust
Definition GLGizmoTextureDisplacement.hpp:558
DecodedHeightTexture m_adjust_source
Definition GLGizmoTextureDisplacement.hpp:568
const void * m_adjust_source_image
Definition GLGizmoTextureDisplacement.hpp:572
TextureAdjustments m_adjust_applied
Definition GLGizmoTextureDisplacement.hpp:561
bool m_adjust_preview_valid
Definition GLGizmoTextureDisplacement.hpp:588
int m_adjust_slot
Definition GLGizmoTextureDisplacement.hpp:557

References Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, Slic3r::TextureAdjustments::identity(), invalidate_adjust_textures(), m_adjust, m_adjust_applied, m_adjust_preview_job, m_adjust_preview_source, m_adjust_preview_tex, m_adjust_preview_valid, m_adjust_slot, m_adjust_source, m_adjust_source_image, m_adjust_volume_id, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::GUI::GLGizmoBase::m_state, Slic3r::GUI::GLGizmoBase::On, Slic3r::TextureDisplacementLayer::preview_adjust, rebuild_preview(), Slic3r::TextureDisplacementLayer::slot, and Slic3r::ModelVolume::texture_displacement_layers.

Referenced by commit_image_adjust(), on_shutdown(), open_image_adjust(), and render_image_adjust_window().

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

◆ collect_paint_region()

bool Slic3r::GUI::GLGizmoTextureDisplacement::collect_paint_region ( const TriangleMesh & mesh,
const TextureDisplacementFacetsData & facets,
std::vector< uint8_t > & region,
std::array< LayerPaintMap, TEXTURE_DISPLACEMENT_MAX_LAYERS > * paint )
staticprivate
5447{
5448 const indexed_triangle_set &its = mesh.its;
5449 const size_t ntri = its.indices.size();
5450 const size_t nvert = its.vertices.size();
5451
5452 region.assign(ntri, 0);
5453 if (paint)
5454 for (LayerPaintMap &pm : *paint)
5455 pm = LayerPaintMap{};
5456
5457 // Twice the area of each source triangle, for the "is this one covered edge to edge" test below.
5458 // Only the paint carry-forward needs it, and the live subdivide preview calls this on every
5459 // slider frame, so it is not built for the region-only path.
5460 const auto tri_area2 = [](const Vec3f &a, const Vec3f &b, const Vec3f &c) {
5461 return (b - a).cross(c - a).norm();
5462 };
5463 std::vector<float> source_area2;
5464 if (paint) {
5465 source_area2.resize(ntri);
5466 for (size_t i = 0; i < ntri; ++i)
5467 source_area2[i] = tri_area2(its.vertices[size_t(its.indices[i][0])],
5468 its.vertices[size_t(its.indices[i][1])],
5469 its.vertices[size_t(its.indices[i][2])]);
5470 }
5471
5472 bool any_paint = false;
5473 for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) {
5474 const TriangleSelector::TriangleSplittingData &data = facets[size_t(slot)];
5476 continue;
5477
5478 // The refine region is exactly the original triangles the brush touched. `triangles_to_split`
5479 // lists precisely those: serialize() records an entry for every original triangle that is
5480 // either split (i.e. partially painted, which is the patch boundary) or carries a non-default
5481 // state (fully painted). No dilation - an earlier version marked every triangle sharing a
5482 // *vertex* with the patch, which on a coarse model pulls in a whole fan of huge unpainted
5483 // neighbours and then refines them to the resolution floor, since the height field the detail
5484 // test samples is not restricted to the painted area. The conformal closure inside
5485 // subdivide_mesh_adaptive() already grades the size change outward on its own.
5486 for (const TriangleSelector::TriangleBitStreamMapping &m : data.triangles_to_split)
5487 if (size_t(m.triangle_idx) < ntri)
5488 region[m.triangle_idx] |= REFINE_PAINTED;
5489
5490 if (paint) {
5491 TriangleSelector sel(mesh);
5492 sel.deserialize(data, false);
5493 // get_facets_strict() now reports which source triangle each sub-triangle came from, which
5494 // is what lets partial coverage be carried forward geometrically instead of being rounded
5495 // away. Note a *fully* painted source can still come back as several sub-triangles (T-joint
5496 // splits forced by a refined neighbour), so "wholly painted" is an area test, not a
5497 // one-piece test.
5498 std::vector<int> src;
5499 const indexed_triangle_set patch = sel.get_facets_strict(EnforcerBlockerType::ENFORCER, &src);
5500
5501 LayerPaintMap &pm = (*paint)[slot];
5502 pm.full.assign(ntri, 0);
5503 pm.part_start.assign(ntri + 1, 0);
5504
5505 std::vector<float> covered2(ntri, 0.f);
5506 for (size_t j = 0; j < patch.indices.size() && j < src.size(); ++j) {
5507 if (size_t(src[j]) >= ntri)
5508 continue;
5509 const stl_triangle_vertex_indices &t = patch.indices[j];
5510 covered2[size_t(src[j])] += tri_area2(patch.vertices[size_t(t[0])], patch.vertices[size_t(t[1])],
5511 patch.vertices[size_t(t[2])]);
5512 }
5513 for (size_t i = 0; i < ntri; ++i)
5514 pm.full[i] = (source_area2[i] > 0.f && covered2[i] >= 0.999f * source_area2[i]) ? 1 : 0;
5515
5516 // Only partly covered sources need their pieces kept - a full one answers every query with
5517 // "painted", and an untouched one with "not painted".
5518 for (size_t j = 0; j < patch.indices.size() && j < src.size(); ++j)
5519 if (size_t(src[j]) < ntri && !pm.full[size_t(src[j])])
5520 ++pm.part_start[size_t(src[j]) + 1];
5521 for (size_t i = 0; i < ntri; ++i)
5522 pm.part_start[i + 1] += pm.part_start[i];
5523 pm.part.resize(size_t(pm.part_start[ntri]));
5524 {
5525 std::vector<int> fill(pm.part_start.begin(), pm.part_start.begin() + ntri);
5526 for (size_t j = 0; j < patch.indices.size() && j < src.size(); ++j) {
5527 const size_t S = size_t(src[j]);
5528 if (S >= ntri || pm.full[S])
5529 continue;
5530 const stl_triangle_vertex_indices &t = patch.indices[j];
5531 pm.part[size_t(fill[S]++)] = { patch.vertices[size_t(t[0])], patch.vertices[size_t(t[1])],
5532 patch.vertices[size_t(t[2])] };
5533 }
5534 }
5535 }
5536 any_paint = true;
5537 }
5538 if (!any_paint)
5539 return false;
5540
5541 // The band straddling the paint's edge. The bake steps the surface from full displacement to zero
5542 // across it, and nothing else in the refinement criteria can see that step: the chord-error
5543 // sampler has no per-point paint test, so just outside the paint it keeps reporting the same
5544 // smooth height field and reports no error at all. Left alone, the transition therefore stays at
5545 // whatever density the input had - which is what makes the rim of an unpainted island a ring of
5546 // big, steeply tilted triangles.
5547 //
5548 // Seeded from the vertices shared by a painted and an unpainted triangle (the actual edge of the
5549 // paint) and grown outward over vertex adjacency, so the band covers both sides of the step.
5550 if (BORDER_BAND_RINGS > 0) {
5551 // Vertex -> incident triangles, CSR-style (counted, prefix-summed, filled). This runs on every
5552 // frame of the subdivide preview's sliders, so it must not allocate a small vector per vertex.
5553 std::vector<int> vstart(nvert + 1, 0);
5554 for (size_t i = 0; i < ntri; ++i)
5555 for (int k = 0; k < 3; ++k)
5556 if (size_t(its.indices[i][k]) < nvert)
5557 ++vstart[size_t(its.indices[i][k]) + 1];
5558 for (size_t v = 0; v < nvert; ++v)
5559 vstart[v + 1] += vstart[v];
5560 // static_cast, not size_t(...): the latter parses as a parameter declaration (see the note
5561 // above the identical prefix sum on `part`).
5562 std::vector<int> vtri(static_cast<size_t>(vstart[nvert]), 0);
5563 {
5564 std::vector<int> fill(vstart.begin(), vstart.begin() + nvert);
5565 for (size_t i = 0; i < ntri; ++i)
5566 for (int k = 0; k < 3; ++k)
5567 if (size_t(its.indices[i][k]) < nvert)
5568 vtri[size_t(fill[size_t(its.indices[i][k])]++)] = int(i);
5569 }
5570
5571 // A vertex used by both a painted and an unpainted triangle sits exactly on the paint's edge.
5572 std::vector<uint8_t> ring_vertex(nvert, 0);
5573 for (size_t v = 0; v < nvert; ++v) {
5574 bool painted = false, unpainted = false;
5575 for (int k = vstart[v]; k < vstart[v + 1]; ++k)
5576 ((region[size_t(vtri[size_t(k)])] & REFINE_PAINTED) ? painted : unpainted) = true;
5577 ring_vertex[v] = (painted && unpainted) ? 1 : 0;
5578 }
5579
5580 for (int ring = 0; ring < BORDER_BAND_RINGS; ++ring) {
5581 std::vector<uint8_t> next = ring_vertex;
5582 for (size_t v = 0; v < nvert; ++v) {
5583 if (!ring_vertex[v])
5584 continue;
5585 for (int k = vstart[v]; k < vstart[v + 1]; ++k) {
5586 const size_t t = size_t(vtri[size_t(k)]);
5587 region[t] |= REFINE_BORDER;
5588 // Grow through this triangle's other corners, for the following ring.
5589 for (int c = 0; c < 3; ++c)
5590 if (size_t(its.indices[t][c]) < nvert)
5591 next[size_t(its.indices[t][c])] = 1;
5592 }
5593 }
5594 ring_vertex.swap(next);
5595 }
5596 }
5597 return true;
5598}
bool has_facets(EnforcerBlockerType state) const
Definition TriangleSelector.cpp:1475
constexpr int BORDER_BAND_RINGS
Definition GLGizmoTextureDisplacement.cpp:186
@ ENFORCER
Definition TriangleSelector.hpp:27
static constexpr size_t TEXTURE_DISPLACEMENT_MAX_LAYERS
Definition TextureDisplacement.hpp:44
static constexpr uint8_t REFINE_BORDER
Definition TextureDisplacement.hpp:35
static constexpr uint8_t REFINE_PAINTED
Definition TextureDisplacement.hpp:34
Definition GLGizmoTextureDisplacement.hpp:407
std::vector< uint8_t > full
Definition GLGizmoTextureDisplacement.hpp:408

References Slic3r::TriangleSelector::deserialize(), Slic3r::ENFORCER, Slic3r::GUI::GLGizmoTextureDisplacement::LayerPaintMap::full, Slic3r::TriangleSelector::get_facets_strict(), Slic3r::TriangleSelector::has_facets(), Slic3r::TriangleMesh::its, Slic3r::GUI::GLGizmoTextureDisplacement::LayerPaintMap::part, Slic3r::GUI::GLGizmoTextureDisplacement::LayerPaintMap::part_start, Slic3r::REFINE_BORDER, Slic3r::REFINE_PAINTED, Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS, Slic3r::TriangleSelector::TriangleBitStreamMapping::triangle_idx, and Slic3r::TriangleSelector::TriangleSplittingData::triangles_to_split.

Referenced by painted_area_mm2(), prepare_mesh(), and rebuild_subdivide_preview().

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

◆ color_settings_for()

TextureColorSettings Slic3r::GUI::GLGizmoTextureDisplacement::color_settings_for ( const ModelVolume & mv)
5709{
5710 TextureColorSettings out;
5711 if (!any_layer_colors(mv))
5712 return out; // nothing is colouring: every colour path stays switched off
5713 out.palette = cached_palette();
5714 out.palette_pure = make_palette(m_palette_filaments, {});
5715 out.despeckle_passes = mv.texture_displacement_options.color_despeckle;
5716 return out;
5717}
static bool any_layer_colors(const ModelVolume &mv)
Definition GLGizmoTextureDisplacement.cpp:5700
const std::vector< PaletteEntry > & cached_palette()
Definition GLGizmoTextureDisplacement.cpp:5719

References any_layer_colors(), cached_palette(), Slic3r::TextureDisplacementOptions::color_despeckle, Slic3r::TextureColorSettings::despeckle_passes, m_palette_filaments, make_palette(), Slic3r::TextureColorSettings::palette, Slic3r::TextureColorSettings::palette_pure, and Slic3r::ModelVolume::texture_displacement_options.

Referenced by bake(), queue_prepare(), queue_preview_job(), and run_stage_debug().

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

◆ commit_image_adjust()

void Slic3r::GUI::GLGizmoTextureDisplacement::commit_image_adjust ( TextureDisplacementLayer & layer)
private
4962{
4963 if (m_adjust_source.empty() || m_adjust.identity()) {
4964 // Nothing was changed, so there is nothing to save.
4966 return;
4967 }
4968
4969 // The full-size pass, once: 13-19 ms grayscale / 40-60 ms colour on a 1024x1024 with every knob
4970 // engaged. Run on the UI thread deliberately, unlike the live preview: this is a single click, and the
4971 // rebuild that follows it - a CPU mesh displacement of the painted patch - is already an order of
4972 // magnitude more work, synchronously, so moving only this off the thread would buy nothing a user
4973 // could perceive.
4974 const DecodedHeightTexture adjusted = apply_texture_adjustments(m_adjust_source, m_adjust);
4975
4976 std::string error;
4977 const std::string stem = layer.name.empty() ? std::string("texture") : layer.name;
4978 const std::optional<TextureLibraryEntry> entry = save_texture_to_library(adjusted, stem, error);
4979 if (!entry) {
4980 show_error(nullptr, error);
4981 return; // the panel stays open, and the layer keeps the live adjustment: the edit is not lost
4982 }
4983
4984 // Cleared *before* the layer is pointed at the new file, and this ordering is the whole correctness of
4985 // the commit: the saved image already has the adjustment baked into its pixels, so leaving
4986 // preview_adjust set would apply brightness, contrast and the rest a second time on top of itself.
4987 layer.preview_adjust = TextureAdjustments{};
4988 // set_layer_texture() does the rest - the name, the recently-used ordering, a fresh image_data buffer
4989 // (which is what invalidates every cache keyed by the old pointer), the undo snapshot and the rebuild.
4990 set_layer_texture(layer, *entry);
4991
4993}
void set_layer_texture(TextureDisplacementLayer &layer, const TextureLibraryEntry &entry)
Definition GLGizmoTextureDisplacement.cpp:4313
void close_image_adjust()
Definition GLGizmoTextureDisplacement.cpp:4872
std::optional< TextureLibraryEntry > save_texture_to_library(const DecodedHeightTexture &image, const std::string &stem, std::string &error)
Definition TextureLibrary.cpp:465
DecodedHeightTexture apply_texture_adjustments(const DecodedHeightTexture &src, const TextureAdjustments &adj)
Definition TextureDisplacement.cpp:550

References Slic3r::apply_texture_adjustments(), close_image_adjust(), m_adjust, m_adjust_source, Slic3r::TextureDisplacementLayer::name, Slic3r::TextureDisplacementLayer::preview_adjust, Slic3r::GUI::save_texture_to_library(), set_layer_texture(), and Slic3r::GUI::show_error().

Referenced by render_image_adjust_window().

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

◆ compute_layer_corner_uvs()

std::vector< Vec2f > Slic3r::GUI::GLGizmoTextureDisplacement::compute_layer_corner_uvs ( const indexed_triangle_set & patch,
const TextureDisplacementLayer & layer,
const std::vector< int > * patch_source = nullptr ) const
private
1426{
1427 if (layer.projection_method == TextureProjectionMethod::ModelUV)
1428 return model_uv_corners(local_patch, layer, patch_source);
1429 if (layer.projection_method == TextureProjectionMethod::LSCM) {
1430 const indexed_triangle_set patch = patch_in_world(local_patch);
1431 const float aspect = layer_texture_aspect(layer);
1432 std::vector<Vec2f> uv = compute_lscm_corner_uvs(patch, layer);
1433 for (Vec2f &p : uv)
1434 p = apply_uv_transform(p, layer, aspect);
1435 return uv;
1436 }
1437 // Single-valued per point: fan the per-vertex result out over the corners.
1438 const std::vector<Vec2f> per_vertex = compute_layer_vertex_uvs(local_patch, layer, patch_source);
1439 if (per_vertex.size() != local_patch.vertices.size())
1440 return {};
1441 std::vector<Vec2f> corner(local_patch.indices.size() * 3);
1442 for (size_t f = 0; f < local_patch.indices.size(); ++f)
1443 for (int k = 0; k < 3; ++k)
1444 corner[f * 3 + size_t(k)] = per_vertex[size_t(local_patch.indices[f][k])];
1445 return corner;
1446}
std::vector< Vec2f > compute_layer_vertex_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer, const std::vector< int > *patch_source=nullptr) const
Definition GLGizmoTextureDisplacement.cpp:1355
indexed_triangle_set patch_in_world(const indexed_triangle_set &patch) const
Definition GLGizmoTextureDisplacement.cpp:1321
std::vector< Vec2f > model_uv_corners(const indexed_triangle_set &local_patch, const TextureDisplacementLayer &layer, const std::vector< int > *patch_source) const
Definition GLGizmoTextureDisplacement.cpp:1337
Vec2f apply_uv_transform(const Vec2f &planar, const TextureDisplacementLayer &layer, float aspect, bool normalized_uv)
Definition TextureDisplacement.cpp:2206
static double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
Definition FillGyroid.cpp:124
std::vector< Vec2f > compute_lscm_corner_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer)
Definition TextureDisplacement.cpp:2024
@ LSCM
Definition TextureDisplacement.hpp:82

References Slic3r::apply_uv_transform(), compute_layer_vertex_uvs(), Slic3r::compute_lscm_corner_uvs(), Slic3r::f(), layer_texture_aspect(), Slic3r::LSCM, model_uv_corners(), Slic3r::ModelUV, patch_in_world(), and Slic3r::TextureDisplacementLayer::projection_method.

Referenced by rebuild_shaded_preview_mesh(), and rebuild_uvcheck_mesh().

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

◆ compute_layer_vertex_uvs()

std::vector< Vec2f > Slic3r::GUI::GLGizmoTextureDisplacement::compute_layer_vertex_uvs ( const indexed_triangle_set & patch,
const TextureDisplacementLayer & layer,
const std::vector< int > * patch_source = nullptr ) const
private
1358{
1359 if (layer.projection_method == TextureProjectionMethod::ModelUV)
1360 // Collapsed to one per vertex the same way the unwrap is - a vertex has one position, so the
1361 // displacement can only read one height for it.
1362 return collapse_corner_uvs(local_patch, model_uv_corners(local_patch, layer, patch_source));
1363
1364 // The bake maps the texture in world millimetres, so "Tile size (mm)" means the same thing on a
1365 // scaled instance as it does on an untouched one (see build_texture_displacement()). Everything
1366 // that has to agree with the bake - the fast preview's uvs, the checker/distortion overlays -
1367 // therefore has to project from the same world positions, not from the volume's own.
1368 const indexed_triangle_set patch = patch_in_world(local_patch);
1369 const float aspect = layer_texture_aspect(layer);
1370 if (layer.projection_method == TextureProjectionMethod::LSCM) {
1371 // compute_lscm_uvs() returns the unwrap's own (raw, mm) coordinates with the island placement
1372 // folded in - it does *not* apply the layer's tiling/rotation/offset. The bake applies those
1373 // on top (sample_layer_height()'s lscm branch runs the result through sample_at()), so the
1374 // shader's precomputed-uv path has to as well, or the fast preview samples millimetre-valued
1375 // coordinates as if they were uv and shows the texture at a wildly wrong scale.
1376 std::vector<Vec2f> uv = compute_lscm_uvs(patch, layer);
1377 for (Vec2f &p : uv)
1378 p = apply_uv_transform(p, layer, aspect);
1379 return uv;
1380 }
1381 if (layer.projection_method == TextureProjectionMethod::ViewProjected) {
1382 std::vector<Vec2f> uv(patch.vertices.size());
1383 for (size_t vi = 0; vi < patch.vertices.size(); ++vi) {
1384 if (layer.view_project_projective) {
1385 // Matches sample_layer_height()'s projective branch, including skipping
1386 // apply_uv_transform(). A vertex behind the projector gets a uv far outside [0,1] so
1387 // it samples as nothing, rather than the mirrored coordinate a blind divide gives.
1388 if (!project_uv_projective(layer.view_project_matrix, patch.vertices[vi], uv[vi]))
1389 uv[vi] = Vec2f(-1e6f, -1e6f);
1390 continue;
1391 }
1392 // Negated up, as sample_layer_height()'s affine branch has it: v runs down the image.
1393 const Vec2f planar(patch.vertices[vi].dot(layer.view_project_right),
1394 -patch.vertices[vi].dot(layer.view_project_up));
1395 uv[vi] = apply_uv_transform(planar, layer, aspect);
1396 }
1397 return uv;
1398 }
1399 return {}; // Triplanar / Cylindrical / Spherical: the shader projects on its own
1400}
std::vector< Vec2f > compute_lscm_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer)
Definition TextureDisplacement.cpp:1993
bool project_uv_projective(const std::array< float, 12 > &m, const Vec3f &position, Vec2f &uv)
Definition TextureDisplacement.cpp:2261
std::vector< Vec2f > collapse_corner_uvs(const indexed_triangle_set &patch, const std::vector< Vec2f > &corner)
Definition TextureDisplacement.cpp:2108

References Slic3r::apply_uv_transform(), Slic3r::collapse_corner_uvs(), Slic3r::compute_lscm_uvs(), layer_texture_aspect(), Slic3r::LSCM, model_uv_corners(), Slic3r::ModelUV, patch_in_world(), Slic3r::project_uv_projective(), Slic3r::TextureDisplacementLayer::projection_method, Slic3r::TextureDisplacementLayer::view_project_matrix, Slic3r::TextureDisplacementLayer::view_project_projective, Slic3r::TextureDisplacementLayer::view_project_right, Slic3r::TextureDisplacementLayer::view_project_up, and Slic3r::ViewProjected.

Referenced by compute_layer_corner_uvs(), and rebuild_uvcheck_mesh().

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

◆ compute_shaded_active_faces()

void Slic3r::GUI::GLGizmoTextureDisplacement::compute_shaded_active_faces ( const std::vector< int > & charts,
size_t patch_face_count )
private
1565{
1566 m_shaded_active_face.clear();
1567 if (charts.empty() || patch_face_count == 0)
1568 return;
1569 const PatchUnwrap &u = m_uv_editor_unwrap;
1570 if (u.source_face.size() != u.indices.size())
1571 return;
1572 m_shaded_active_face.assign(patch_face_count, 0);
1573 // Flag every triangle of every chart being moved. For a group/multi move that is more than one
1574 // chart, but since such a move is a pure translation the shader applies the same delta to them all
1575 // (see on_island_edited) - exactly the "joined islands move together" behaviour.
1576 for (size_t t = 0; t < u.indices.size(); ++t) {
1577 const int f = u.source_face[t];
1578 const int v0 = u.indices[t][0]; // a triangle lies in one chart, so any corner names it
1579 if (f < 0 || size_t(f) >= m_shaded_active_face.size() || v0 < 0 || size_t(v0) >= u.vertex_chart.size())
1580 continue;
1581 if (std::find(charts.begin(), charts.end(), u.vertex_chart[size_t(v0)]) != charts.end())
1582 m_shaded_active_face[size_t(f)] = 1;
1583 }
1584}
std::vector< uint8_t > m_shaded_active_face
Definition GLGizmoTextureDisplacement.hpp:992

References Slic3r::f(), Slic3r::PatchUnwrap::indices, m_shaded_active_face, m_uv_editor_unwrap, Slic3r::PatchUnwrap::source_face, and Slic3r::PatchUnwrap::vertex_chart.

Referenced by rebuild_shaded_preview_mesh().

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

◆ compute_uv_editor_distortion_colors()

void Slic3r::GUI::GLGizmoTextureDisplacement::compute_uv_editor_distortion_colors ( const indexed_triangle_set & patch)
private
2771{
2773 const PatchUnwrap &u = m_uv_editor_unwrap;
2774 if (u.empty() || u.chart_count <= 0)
2775 return;
2776
2777 // log2(uv area / 3D area) per unwrap triangle - the same measure the 3D distortion overlay uses.
2778 std::vector<float> tri_log(u.indices.size(), 0.f);
2779 std::vector<int> tri_chart(u.indices.size(), -1);
2780 for (size_t f = 0; f < u.indices.size(); ++f) {
2781 const stl_triangle_vertex_indices &t = u.indices[f];
2782 if (t[0] < 0 || size_t(t[0]) >= u.vertex_chart.size())
2783 continue;
2784 tri_chart[f] = u.vertex_chart[size_t(t[0])];
2785 const auto p3 = [&](int uv_idx) -> Vec3f {
2786 const int sv = (size_t(uv_idx) < u.source_vertex.size()) ? u.source_vertex[size_t(uv_idx)] : -1;
2787 return (sv >= 0 && size_t(sv) < patch.vertices.size()) ? patch.vertices[size_t(sv)] : Vec3f::Zero();
2788 };
2789 const float a3 = 0.5f * (p3(t[1]) - p3(t[0])).cross(p3(t[2]) - p3(t[0])).norm();
2790 const Vec2f e0 = u.uvs[size_t(t[1])] - u.uvs[size_t(t[0])];
2791 const Vec2f e1 = u.uvs[size_t(t[2])] - u.uvs[size_t(t[0])];
2792 const float a2 = 0.5f * std::abs(e0.x() * e1.y() - e0.y() * e1.x());
2793 tri_log[f] = (a3 > 1e-12f && a2 > 1e-12f) ? std::log2(a2 / a3) : 0.f;
2794 }
2795
2796 // Centre on the median stretch, so a globally scaled unwrap reads as uniform and only *relative*
2797 // stretching shows up - matching the 3D overlay's convention.
2798 std::vector<float> sorted = tri_log;
2799 float median = 0.f;
2800 if (!sorted.empty()) {
2801 std::nth_element(sorted.begin(), sorted.begin() + sorted.size() / 2, sorted.end());
2802 median = sorted[sorted.size() / 2];
2803 }
2804
2805 std::vector<double> chart_sum(size_t(u.chart_count), 0.0);
2806 std::vector<int> chart_cnt(size_t(u.chart_count), 0);
2807 for (size_t f = 0; f < tri_log.size(); ++f) {
2808 const int c = tri_chart[f];
2809 if (c >= 0 && c < u.chart_count) {
2810 chart_sum[size_t(c)] += tri_log[f];
2811 ++chart_cnt[size_t(c)];
2812 }
2813 }
2814
2815 // Blue (compressed) -> green (ideal) -> red (stretched), +/- 2 stops spanning the full range.
2816 const auto heat = [](float t) -> ColorRGBA {
2817 t = std::clamp(t, 0.f, 1.f);
2818 const ColorRGBA blue{ 0.15f, 0.35f, 1.0f, 0.5f }, green{ 0.2f, 0.9f, 0.3f, 0.5f }, red{ 1.0f, 0.2f, 0.15f, 0.5f };
2819 if (t < 0.5f) { const float s = t * 2.f; return blue * (1.f - s) + green * s; }
2820 const float s = (t - 0.5f) * 2.f; return green * (1.f - s) + red * s;
2821 };
2822
2823 m_uv_editor_distortion_colors.resize(size_t(u.chart_count), heat(0.5f));
2824 for (int c = 0; c < u.chart_count; ++c)
2825 if (chart_cnt[size_t(c)] > 0) {
2826 const float avg = float(chart_sum[size_t(c)] / chart_cnt[size_t(c)]);
2827 m_uv_editor_distortion_colors[size_t(c)] = heat(std::clamp(0.5f + (avg - median) / 4.f, 0.f, 1.f));
2828 }
2829}
std::vector< ColorRGBA > m_uv_editor_distortion_colors
Definition GLGizmoTextureDisplacement.hpp:1091
T cross(const boost::geometry::model::d2::point_xy< T > &v1, const boost::geometry::model::d2::point_xy< T > &v2)
Definition ExtrusionSimulator.cpp:168

References Slic3r::PatchUnwrap::chart_count, Slic3r::cross(), Slic3r::PatchUnwrap::empty(), Slic3r::f(), Slic3r::PatchUnwrap::indices, m_uv_editor_distortion_colors, m_uv_editor_unwrap, Slic3r::PatchUnwrap::source_vertex, Slic3r::PatchUnwrap::uvs, and Slic3r::PatchUnwrap::vertex_chart.

Referenced by update_uv_editor().

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

◆ cut_island()

void Slic3r::GUI::GLGizmoTextureDisplacement::cut_island ( TextureDisplacementLayer & layer,
int chart )
private
3297{
3298 const ModelVolume *mv = texture_volume();
3299 if (mv == nullptr)
3300 return;
3301 const std::vector<Vec3f> &verts = mv->mesh().its.vertices;
3302 const PatchUnwrap &u = m_uv_editor_unwrap;
3303
3304 // Collect the chart's triangles back in mesh-vertex space, and its 3D bounding box.
3305 std::vector<std::array<int, 3>> tris;
3306 Vec3f lo(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
3307 Vec3f hi(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
3308 for (const stl_triangle_vertex_indices &t : u.indices) {
3309 if (t[0] < 0 || size_t(t[0]) >= u.vertex_chart.size() || u.vertex_chart[size_t(t[0])] != chart)
3310 continue;
3311 std::array<int, 3> bt{};
3312 bool ok = true;
3313 for (int k = 0; k < 3; ++k) {
3314 const int uvv = t[k];
3315 if (uvv < 0 || size_t(uvv) >= u.source_vertex.size()) { ok = false; break; }
3316 const int base = u.source_vertex[size_t(uvv)];
3317 if (base < 0 || size_t(base) >= verts.size()) { ok = false; break; }
3318 bt[k] = base;
3319 lo = lo.cwiseMin(verts[size_t(base)]);
3320 hi = hi.cwiseMax(verts[size_t(base)]);
3321 }
3322 if (ok)
3323 tris.push_back(bt);
3324 }
3325 if (tris.empty())
3326 return;
3327
3328 // Cut perpendicular to the longest axis, through the centroid - a long thin island is split
3329 // across its narrow middle, which is exactly the "islands might be very long" case.
3330 const Vec3f ext = hi - lo;
3331 const int axis = (ext.x() >= ext.y() && ext.x() >= ext.z()) ? 0 : (ext.y() >= ext.z() ? 1 : 2);
3332 const float mid = 0.5f * (lo[axis] + hi[axis]);
3333
3334 std::set<std::pair<int, int>> seams(layer.lscm_seam_edges.begin(), layer.lscm_seam_edges.end());
3335 for (const std::array<int, 3> &bt : tris)
3336 for (int i = 0; i < 3; ++i) {
3337 const int a = bt[i], b = bt[(i + 1) % 3];
3338 // Endpoints on opposite sides of the plane -> this edge crosses it -> make it a seam.
3339 if ((verts[size_t(a)][axis] < mid) != (verts[size_t(b)][axis] < mid))
3340 seams.insert({ std::min(a, b), std::max(a, b) });
3341 }
3342 layer.lscm_seam_edges.assign(seams.begin(), seams.end());
3343}

References Slic3r::PatchUnwrap::indices, Slic3r::TriangleMesh::its, Slic3r::TextureDisplacementLayer::lscm_seam_edges, m_uv_editor_unwrap, Slic3r::ModelVolume::mesh(), Slic3r::PatchUnwrap::source_vertex, texture_volume(), and Slic3r::PatchUnwrap::vertex_chart.

Referenced by run_uv_command().

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

◆ data_changed()

void Slic3r::GUI::GLGizmoPainterBase::data_changed ( bool is_serializing)
overridevirtualinherited

Is called when data (Selection) is changed.

Reimplemented from Slic3r::GUI::GLGizmoBase.

72{
73 if (m_state != On)
74 return;
75
76 const ModelObject* mo = m_c->selection_info() ? m_c->selection_info()->model_object() : nullptr;
77 const Selection & selection = m_parent.get_selection();
78 if (mo && selection.is_from_single_instance()
79 && (m_schedule_update || mo->id() != m_old_mo_id || mo->volumes.size() != m_old_volumes_size))
80 {
81 //BBS: add logic to distinguish the first_time_update and later_update
83
84 m_old_mo_id = mo->id();
85 m_old_volumes_size = mo->volumes.size();
86 m_schedule_update = false;
87 }
88}
ObjectID m_old_mo_id
Definition GLGizmoPainterBase.hpp:199
size_t m_old_volumes_size
Definition GLGizmoPainterBase.hpp:200
virtual void update_from_model_object(bool first_update)=0
bool m_schedule_update
Definition GLGizmoPainterBase.hpp:356

References Slic3r::ObjectBase::id(), Slic3r::GUI::Selection::is_from_single_instance(), Slic3r::GUI::GLGizmoBase::m_c, m_old_mo_id, m_old_volumes_size, Slic3r::GUI::GLGizmoBase::m_parent, m_schedule_update, Slic3r::GUI::GLGizmoBase::m_state, Slic3r::GUI::GLGizmoBase::On, update_from_model_object(), and Slic3r::ModelObject::volumes.

Referenced by Slic3r::GUI::GLGizmoMmuSegmentation::data_changed().

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

◆ do_stop_dragging()

void Slic3r::GUI::GLGizmoBase::do_stop_dragging ( bool perform_mouse_cleanup)
protectedinherited
494{
495 for (auto& grabber : m_grabbers) grabber.dragging = false;
496 m_dragging = false;
497
498 // NOTE: This should be part of GLCanvas3D
499 // Reset hover_id when leave window
500 if (perform_mouse_cleanup) m_parent.mouse_up_cleanup();
501
503
504 // There is prediction that after draggign, data are changed
505 // Data are updated twice also by canvas3D::reload_scene.
506 // Should be fixed.
507 m_parent.get_gizmos_manager().update_data();
508
509 wxGetApp().obj_manipul()->set_dirty();
510
511 // Let the plater know that the dragging finished, so a delayed
512 // refresh of the scene with the background processing data should
513 // be performed.
514 m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
515 // updates camera target constraints
516 m_parent.refresh_camera_scene_box();
517}
bool m_dragging
Definition GLGizmoBase.hpp:151
std::vector< Grabber > m_grabbers
Definition GLGizmoBase.hpp:152
virtual void on_stop_dragging()
Definition GLGizmoBase.hpp:254

References m_dragging, m_grabbers, m_parent, and on_stop_dragging().

Referenced by use_grabbers().

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

◆ ease_wheel_scroll()

void Slic3r::GUI::GLGizmoTextureDisplacement::ease_wheel_scroll ( float & target,
float & applied )
private
4357{
4358 ImGuiIO &io = ImGui::GetIO();
4359 const float scroll_now = ImGui::GetScrollY();
4360 const float scroll_max = ImGui::GetScrollMaxY();
4361
4362 // Anything that moved the scroll without us - dragging the grab, a keyboard/gamepad nav step, the
4363 // content shrinking under a clamped offset, a SetScroll*() call - has to re-seed the target, or the
4364 // easing below would immediately drag the view back to where it last animated to.
4365 if (applied < 0.f || std::abs(scroll_now - applied) > 0.5f)
4366 target = scroll_now;
4367
4368 if (io.MouseWheel != 0.f && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows))
4369 target -= io.MouseWheel * ImGui::GetFontSize() * 4.f;
4370 // Whole pixels: ImGui floors whatever SetScrollY() is given, so a fractional target could never be
4371 // reached and the "still gliding" test below would stay true forever, repainting the canvas for good.
4372 target = std::floor(std::clamp(target, 0.f, scroll_max));
4373
4374 const float delta = target - scroll_now;
4375 if (std::abs(delta) >= 1.f) {
4376 // Exponential ease, formulated against the frame time so the glide takes the same wall time
4377 // whether the canvas is running at 30 or 144 fps. The last sub-pixel step would be floored
4378 // away, so land on the target outright once the remainder is that small.
4379 const float t = 1.f - std::exp(-20.f * std::clamp(io.DeltaTime, 1.f / 240.f, 1.f / 15.f));
4380 const float step = (std::abs(delta * t) < 1.f) ? delta : delta * t;
4381 const float next = std::floor(scroll_now + step);
4382 ImGui::SetScrollY(next);
4383 applied = next;
4384 m_parent.set_as_dirty(); // nothing else would redraw mid-glide once the mouse stops
4385 } else {
4386 applied = scroll_now;
4387 }
4388}
void step(Coord &crd, const Dir d, const long n=1)
Definition MarchingSquares.hpp:213

References Slic3r::GUI::GLGizmoBase::m_parent.

Referenced by on_render_input_window(), and render_texture_library_popup().

Here is the caller graph for this function:

◆ ensure_panel_icons()

void Slic3r::GUI::GLGizmoTextureDisplacement::ensure_panel_icons ( )
private
4011{
4013 return;
4014 m_panel_icons_tried = true;
4015
4016 // Order is irrelevant; the map keys by file name. Loaded with color_wite_gray so each icon has both
4017 // a monochrome ("normal", grey in the current theme) and an original-colour variant.
4018 static const std::vector<std::string> names = {
4019 "texture_displacement_brush.svg", "texture_displacement_face.svg", "texture_displacement_connected_area.svg",
4020 "texture_displacement_real_preview.svg", "texture_displacement_fast_preview.svg",
4021 "texture_displacement_checker.svg", "texture_displacement_distortion.svg",
4022 "texture_displacement_wireframe.svg", "texture_displacement_cross.svg",
4023 "texture_displacement_uv_select_island.svg", "texture_displacement_uv_select_edge.svg",
4024 "cog.svg", // the layer card's "adjust image" button
4025 "texture_displacement_uv_select_vertex.svg",
4026 // Paint / Erase, brush cursor shape, mapping, tiling, placement and the dock toggle.
4027 "texture_displacement_add.svg", "texture_displacement_add_negative.svg", "circle_paint.svg",
4028 "menu_obj_sphere.svg", "menu_obj_cube.svg", "menu_obj_cylinder.svg", "texture_displacement_map_unwrap.svg",
4029 "texture_displacement_map_view.svg", "texture_displacement_map_model_uv.svg",
4030 "texture_displacement_tile_repeat.svg", "menu_mirror_x.svg",
4031 "texture_displacement_adjust.svg", "canvas_drag.svg", "texture_displacement_move_up.svg",
4032 "texture_displacement_move_down.svg", "texture_displacement_drag.svg",
4033 "texture_displacement_select_all.svg", "texture_displacement_erase_all.svg",
4034 // The "type a value instead" icon at the right end of every slider.
4035 "edit.svg",
4036 };
4037 std::vector<std::string> paths;
4038 paths.reserve(names.size());
4039 for (const std::string &n : names)
4040 paths.push_back(resources_dir() + "/images/" + n);
4041
4042 // Runs from the panel render, i.e. with a GL context current, so the upload is safe here.
4043 //
4044 // Rasterized at twice GLToolbar::Default_Icons_Size rather than at it: these are drawn at the
4045 // toolbar's icon size, which is that constant scaled by DPI (toolbar_icon_scale() folds in
4046 // em_unit), so on a 200% display the draw size reaches 80 px. Rasterizing at 40 would upscale a
4047 // 40 px bitmap there, which is what actually reads as blurry - downscaling does not. The icon set
4048 // is rasterized once for the gizmo's lifetime, so it cannot re-raster on a DPI change; sizing for
4049 // the larger case and letting ImGui shrink it is the version that looks right on both.
4050 const std::vector<IconManager::Icons> icons =
4053 for (size_t i = 0; i < names.size() && i < icons.size(); ++i)
4054 m_panel_icon_map[names[i]] = icons[i];
4055}
bool m_panel_icons_tried
Definition GLGizmoTextureDisplacement.hpp:1246
IconManager m_panel_icons
Definition GLGizmoTextureDisplacement.hpp:1244
std::map< std::string, IconManager::Icons > m_panel_icon_map
Definition GLGizmoTextureDisplacement.hpp:1245
static const float Default_Icons_Size
Definition GLToolbar.hpp:270
@ color_wite_gray
Definition IconManager.hpp:33
const std::string & resources_dir()
Definition utils.cpp:296

References Slic3r::GUI::IconManager::color_wite_gray, Slic3r::GUI::GLToolbar::Default_Icons_Size, m_panel_icon_map, m_panel_icons, m_panel_icons_tried, and Slic3r::resources_dir().

Referenced by on_render_input_window().

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

◆ estimated_refined_triangles()

size_t Slic3r::GUI::GLGizmoTextureDisplacement::estimated_refined_triangles ( const ModelVolume & mv,
float edge_mm )
private
5669{
5670 if (edge_mm <= 0.f)
5671 return 0;
5672 return size_t(4.0 * painted_area_mm2(mv) / (double(edge_mm) * double(edge_mm)));
5673}
double painted_area_mm2(const ModelVolume &mv)
Definition GLGizmoTextureDisplacement.cpp:5637

References painted_area_mm2().

Here is the call graph for this function:

◆ exit_debug_view()

void Slic3r::GUI::GLGizmoTextureDisplacement::exit_debug_view ( )
private
6823{
6824 const bool was_showing = m_debug_stage >= 0;
6825 m_debug_stage = -1;
6826 m_debug_stages.clear();
6827 m_debug_stages.shrink_to_fit();
6828 if (was_showing)
6829 rebuild_preview(); // hands m_preview_glmodel back to the live preview
6830}
int m_debug_stage
Definition GLGizmoTextureDisplacement.hpp:865
std::vector< BakeStageSnapshot > m_debug_stages
Definition GLGizmoTextureDisplacement.hpp:862

References m_debug_stage, m_debug_stages, and rebuild_preview().

Referenced by render_debug_stage_panel(), and run_stage_debug().

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

◆ facets_data_of()

TextureDisplacementFacetsData Slic3r::GUI::GLGizmoTextureDisplacement::facets_data_of ( const ModelVolume & mv)
static
5693{
5695 for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i)
5696 out[size_t(i)] = mv.texture_displacement_facet(i).get_data();
5697 return out;
5698}
std::array< TriangleSelector::TriangleSplittingData, TEXTURE_DISPLACEMENT_MAX_LAYERS > TextureDisplacementFacetsData
Definition TextureDisplacement.hpp:913

References Slic3r::FacetsAnnotation::get_data(), Slic3r::ModelVolume::texture_displacement_facet(), and Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS.

Referenced by painted_area_mm2(), queue_prepare(), and rebuild_subdivide_preview().

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

◆ filament_palette()

std::vector< ColorRGBA > Slic3r::GUI::GLGizmoTextureDisplacement::filament_palette ( )
static
5791{
5792 std::vector<ColorRGBA> all = wxGetApp().plater()->get_extruders_colors();
5793
5794 // Physical filaments only. A mix bakes into a mixed filament slot of its own, and those slots are
5795 // extruders too - mixing them again would hand a blend components naming a virtual slot, where it
5796 // can only name physical ones ("Mixed filament has invalid or mismatched components"). Mixed slots
5797 // are kept after the physical ones, so filament i here is extruder i.
5798 const auto *is_mixed = wxGetApp().preset_bundle->project_config.option<ConfigOptionBools>("filament_is_mixed");
5799 std::vector<ColorRGBA> palette;
5800 palette.reserve(all.size());
5801 for (size_t i = 0; i < all.size(); ++i)
5802 if (is_mixed == nullptr || i >= is_mixed->values.size() || !is_mixed->values[i])
5803 palette.push_back(all[i]);
5804
5805 // A paint mask can only name so many states, and every mix spends one beside these.
5806 if (palette.size() > size_t(EnforcerBlockerType::ExtruderMax))
5808 return palette;
5809}
void resize(size_t n, const ConfigOption *opt_default=nullptr) override
Definition Config.hpp:687
ConfigOptionBoolsTempl< false > ConfigOptionBools
Definition Config.hpp:2066
@ all
Definition MarchingSquares.hpp:194

References Slic3r::ExtruderMax.

Referenced by cached_palette().

Here is the caller graph for this function:

◆ follow_island_highlight()

void Slic3r::GUI::GLGizmoTextureDisplacement::follow_island_highlight ( )
private
2888{
2889 const std::vector<int> *islands = island_highlight_selection();
2890 const int view = m_island_view.step(islands != nullptr && !islands->empty(), view_mode(), m_debug_stage >= 0);
2891 if (view >= 0)
2892 apply_view_mode(view);
2893}
void apply_view_mode(int mode)
Definition GLGizmoTextureDisplacement.cpp:2845
IslandViewFollow m_island_view
Definition GLGizmoTextureDisplacement.hpp:696
const std::vector< int > * island_highlight_selection()
Definition GLGizmoTextureDisplacement.cpp:2065
int view_mode() const
Definition GLGizmoTextureDisplacement.cpp:2864

References apply_view_mode(), island_highlight_selection(), m_debug_stage, m_island_view, and view_mode().

Referenced by on_render_input_window().

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

◆ format()

std::string Slic3r::GUI::GLGizmoBase::format ( float value,
unsigned int decimals ) const
protectedinherited

◆ get_action_snapshot_name()

std::string Slic3r::GUI::GLGizmoTextureDisplacement::get_action_snapshot_name ( ) const
inlineoverrideprotectedvirtual

Reimplemented from Slic3r::GUI::GLGizmoBase.

284{ return _u8L("Texture displacement editing"); }

References _u8L.

◆ get_clipping_plane_data()

GLGizmoPainterBase::ClippingPlaneDataWrapper Slic3r::GUI::GLGizmoPainterBase::get_clipping_plane_data ( ) const
protectedinherited
91{
92 ClippingPlaneDataWrapper clp_data_out{{0.f, 0.f, 1.f, FLT_MAX}, {-FLT_MAX, FLT_MAX}};
93 // Take care of the clipping plane. The normal of the clipping plane is
94 // saved with opposite sign than we need to pass to OpenGL (FIXME)
95 if (bool clipping_plane_active = m_c->object_clipper()->get_position() != 0.; clipping_plane_active) {
96 const ClippingPlane *clp = m_c->object_clipper()->get_clipping_plane();
97 for (size_t i = 0; i < 3; ++i)
98 clp_data_out.clp_dataf[i] = -1.f * float(clp->get_data()[i]);
99 clp_data_out.clp_dataf[3] = float(clp->get_data()[3]);
100 }
101
102 // z_range is calculated in the same way as in GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
103 if (m_c->get_canvas()->get_use_clipping_planes()) {
104 const std::array<ClippingPlane, 2> &clps = m_c->get_canvas()->get_clipping_planes();
105 clp_data_out.z_range = {float(-clps[0].get_data()[3]), float(clps[1].get_data()[3])};
106 }
107
108 return clp_data_out;
109}

References Slic3r::GUI::GLGizmoPainterBase::ClippingPlaneDataWrapper::clp_dataf, Slic3r::GUI::ClippingPlane::get_data(), Slic3r::GUI::GLGizmoBase::m_c, and Slic3r::GUI::GLGizmoPainterBase::ClippingPlaneDataWrapper::z_range.

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::render_painted_colors(), Slic3r::GUI::GLGizmoTextureDisplacement::render_shaded_preview_mesh(), render_triangles(), and Slic3r::GUI::GLGizmoTextureDisplacement::render_uvcheck_mesh().

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

◆ get_clipping_plane_in_volume_coordinates()

TriangleSelector::ClippingPlane Slic3r::GUI::GLGizmoPainterBase::get_clipping_plane_in_volume_coordinates ( const Transform3d & trafo) const
protectedinherited
1243 {
1244 const ::Slic3r::GUI::ClippingPlane *const clipping_plane = m_c->object_clipper()->get_clipping_plane();
1245 if (clipping_plane == nullptr || !clipping_plane->is_active())
1246 return {};
1247
1248 const Vec3d clp_normal = clipping_plane->get_normal();
1249 const double clp_offset = clipping_plane->get_offset();
1250
1251 const Transform3d trafo_normal = Transform3d(trafo.linear().transpose());
1252 const Transform3d trafo_inv = trafo.inverse();
1253
1254 Vec3d point_on_plane = clp_normal * clp_offset;
1255 Vec3d point_on_plane_transformed = trafo_inv * point_on_plane;
1256 Vec3d normal_transformed = trafo_normal * clp_normal;
1257 auto offset_transformed = float(point_on_plane_transformed.dot(normal_transformed));
1258
1259 return TriangleSelector::ClippingPlane({float(normal_transformed.x()), float(normal_transformed.y()), float(normal_transformed.z()), offset_transformed});
1260}

References Slic3r::GUI::GLGizmoBase::m_c.

Referenced by gizmo_event().

Here is the caller graph for this function:

◆ get_count()

int Slic3r::GUI::GLGizmoBase::get_count ( )
inlineinherited
224{ return ++count; }
int count
Definition GLGizmoBase.hpp:294

References count.

◆ get_cursor_height_max()

virtual const float Slic3r::GUI::GLGizmoPainterBase::get_cursor_height_max ( ) const
inlinevirtualinherited
223{ return CursorHeightMax; }
static constexpr float CursorHeightMax
Definition GLGizmoPainterBase.hpp:266

References CursorHeightMax.

Referenced by gizmo_event().

Here is the caller graph for this function:

◆ get_cursor_height_min()

virtual const float Slic3r::GUI::GLGizmoPainterBase::get_cursor_height_min ( ) const
inlinevirtualinherited
222{ return CursorHeightMin; }
static constexpr float CursorHeightMin
Definition GLGizmoPainterBase.hpp:265

References CursorHeightMin.

Referenced by gizmo_event().

Here is the caller graph for this function:

◆ get_cursor_height_step()

virtual const float Slic3r::GUI::GLGizmoPainterBase::get_cursor_height_step ( ) const
inlinevirtualinherited
224{ return CursorHeightStep; }
static constexpr float CursorHeightStep
Definition GLGizmoPainterBase.hpp:267

References CursorHeightStep.

Referenced by gizmo_event().

Here is the caller graph for this function:

◆ get_cursor_hover_color()

virtual ColorRGBA Slic3r::GUI::GLGizmoPainterBase::get_cursor_hover_color ( ) const
inlineprotectedvirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoMmuSegmentation.

254{ return { 0.f, 0.f, 0.f, 0.25f }; }

Referenced by render_cursor_circle(), and render_cursor_sphere().

Here is the caller graph for this function:

◆ get_cursor_radius_max()

virtual const float Slic3r::GUI::GLGizmoPainterBase::get_cursor_radius_max ( ) const
inlinevirtualinherited
218{ return CursorRadiusMax; }
static constexpr float CursorRadiusMax
Definition GLGizmoPainterBase.hpp:263

References CursorRadiusMax.

Referenced by gizmo_event().

Here is the caller graph for this function:

◆ get_cursor_radius_min()

virtual const float Slic3r::GUI::GLGizmoPainterBase::get_cursor_radius_min ( ) const
inlinevirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoMmuSegmentation, and Slic3r::GUI::GLGizmoSeam.

217{ return CursorRadiusMin; }
static constexpr float CursorRadiusMin
Definition GLGizmoPainterBase.hpp:262

References CursorRadiusMin.

Referenced by gizmo_event().

Here is the caller graph for this function:

◆ get_cursor_radius_step()

virtual const float Slic3r::GUI::GLGizmoPainterBase::get_cursor_radius_step ( ) const
inlinevirtualinherited
219{ return CursorRadiusStep; }
static constexpr float CursorRadiusStep
Definition GLGizmoPainterBase.hpp:264

References CursorRadiusStep.

Referenced by gizmo_event().

Here is the caller graph for this function:

◆ get_cursor_sphere_left_button_color()

virtual ColorRGBA Slic3r::GUI::GLGizmoPainterBase::get_cursor_sphere_left_button_color ( ) const
inlineprotectedvirtualinherited
251{ return { 0.0f, 0.0f, 1.0f, 0.25f }; }

Referenced by render_cursor_circle(), and render_cursor_sphere().

Here is the caller graph for this function:

◆ get_cursor_sphere_right_button_color()

virtual ColorRGBA Slic3r::GUI::GLGizmoPainterBase::get_cursor_sphere_right_button_color ( ) const
inlineprotectedvirtualinherited
252{ return { 1.0f, 0.0f, 0.0f, 0.25f }; }

Referenced by render_cursor_circle(), and render_cursor_sphere().

Here is the caller graph for this function:

◆ get_gizmo_entering_text()

std::string Slic3r::GUI::GLGizmoTextureDisplacement::get_gizmo_entering_text ( ) const
inlineoverrideprotectedvirtual

Reimplemented from Slic3r::GUI::GLGizmoBase.

282{ return _u8L("Entering Texture displacement painting"); }

References _u8L.

◆ get_gizmo_leaving_text()

std::string Slic3r::GUI::GLGizmoTextureDisplacement::get_gizmo_leaving_text ( ) const
inlineoverrideprotectedvirtual

Reimplemented from Slic3r::GUI::GLGizmoBase.

283{ return _u8L("Leaving Texture displacement painting"); }

References _u8L.

◆ get_gizmo_name()

std::string Slic3r::GUI::GLGizmoBase::get_gizmo_name ( )
inlineinherited
225{ return on_get_name(); }
virtual std::string on_get_name() const =0

References on_get_name().

Here is the call graph for this function:

◆ get_hover_id()

int Slic3r::GUI::GLGizmoBase::get_hover_id ( ) const
inlineinherited
206{ return m_hover_id; }
int m_hover_id
Definition GLGizmoBase.hpp:150

References m_hover_id.

Referenced by Slic3r::GUI::GLCanvas3D::_scene_cache_key(), Slic3r::GUI::GLGizmosManager::grabber_contains_mouse(), Slic3r::GUI::GLGizmoBrimEars::on_mouse(), Slic3r::GUI::GLGizmoCut3D::on_mouse(), Slic3r::GUI::GLGizmoMeshBoolean::on_mouse(), and Slic3r::GUI::GLGizmoPainterBase::on_mouse().

Here is the caller graph for this function:

◆ get_icon_filename()

const std::string & Slic3r::GUI::GLGizmoBase::get_icon_filename ( ) const
inlineinherited
180{ return m_icon_filename; }
std::string m_icon_filename
Definition GLGizmoBase.hpp:148

References m_icon_filename.

◆ get_layer_color_texture()

GLTexture * Slic3r::GUI::GLGizmoTextureDisplacement::get_layer_color_texture ( const TextureDisplacementLayer & layer)
private
6565{
6566 if (layer.empty() || !layer.color_enabled)
6567 return nullptr;
6568 if (m_color_tex && m_color_tex_source.lock() == layer.image_data && m_color_tex_smoothing == layer.smoothing)
6569 return m_color_tex.get();
6570
6571 std::unique_ptr<GLTexture> texture = upload_color_texture(decode_height_texture(layer), HEIGHT_TEX_MAX_PX);
6572 if (!texture) {
6573 m_color_tex.reset();
6574 m_color_tex_source.reset();
6575 return nullptr;
6576 }
6577 m_color_tex = std::move(texture);
6578 m_color_tex_source = layer.image_data;
6579 m_color_tex_smoothing = layer.smoothing;
6580 return m_color_tex.get();
6581}
float m_color_tex_smoothing
Definition GLGizmoTextureDisplacement.hpp:1143
std::unique_ptr< GLTexture > m_color_tex
Definition GLGizmoTextureDisplacement.hpp:1141
std::weak_ptr< const std::vector< unsigned char > > m_color_tex_source
Definition GLGizmoTextureDisplacement.hpp:1142
std::unique_ptr< GLTexture > upload_color_texture(const DecodedHeightTexture &decoded, int max_px)
Definition GLGizmoTextureDisplacement.cpp:450
DecodedHeightTexture decode_height_texture(const TextureDisplacementLayer &layer, bool keep_decoded)
Definition TextureDisplacement.cpp:286

References Slic3r::TextureDisplacementLayer::color_enabled, Slic3r::decode_height_texture(), Slic3r::TextureDisplacementLayer::empty(), Slic3r::TextureDisplacementLayer::image_data, m_color_tex, m_color_tex_smoothing, m_color_tex_source, and Slic3r::TextureDisplacementLayer::smoothing.

Referenced by render_shaded_preview_mesh().

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

◆ get_layer_height_texture()

GLTexture * Slic3r::GUI::GLGizmoTextureDisplacement::get_layer_height_texture ( const TextureDisplacementLayer & layer)
private
6584{
6585 if (layer.empty())
6586 return nullptr;
6587
6588 // One slot, not one per layer: the preview shader only ever shades the *active* layer, so a single
6589 // full-resolution upload is enough and the VRAM cost stays at one texture rather than eight.
6590 if (m_height_tex && m_height_tex_source.lock() == layer.image_data && m_height_tex_smoothing == layer.smoothing)
6591 return m_height_tex.get();
6592
6593 std::unique_ptr<GLTexture> texture = upload_height_thumbnail(decode_height_texture(layer), HEIGHT_TEX_MAX_PX);
6594 if (!texture)
6595 return nullptr;
6596
6597 m_height_tex = std::move(texture);
6598 m_height_tex_source = layer.image_data;
6599 m_height_tex_smoothing = layer.smoothing;
6600 return m_height_tex.get();
6601}
std::weak_ptr< const std::vector< unsigned char > > m_height_tex_source
Definition GLGizmoTextureDisplacement.hpp:1136
float m_height_tex_smoothing
Definition GLGizmoTextureDisplacement.hpp:1137
std::unique_ptr< GLTexture > m_height_tex
Definition GLGizmoTextureDisplacement.hpp:1135
std::unique_ptr< GLTexture > upload_height_thumbnail(const DecodedHeightTexture &decoded, int max_px=THUMBNAIL_MAX_PX)
Definition GLGizmoTextureDisplacement.cpp:404

References Slic3r::decode_height_texture(), Slic3r::TextureDisplacementLayer::empty(), Slic3r::TextureDisplacementLayer::image_data, m_height_tex, m_height_tex_smoothing, m_height_tex_source, and Slic3r::TextureDisplacementLayer::smoothing.

Referenced by render_shaded_preview_mesh(), and shaded_preview_ready().

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

◆ get_layer_thumbnail()

GLTexture * Slic3r::GUI::GLGizmoTextureDisplacement::get_layer_thumbnail ( const TextureDisplacementLayer & layer)
private
6541{
6542 if (layer.empty() || layer.slot < 0 || size_t(layer.slot) >= TEXTURE_DISPLACEMENT_MAX_LAYERS)
6543 return nullptr;
6544
6545 const size_t slot = size_t(layer.slot);
6546 if (m_thumbnails[slot] && m_thumbnail_source[slot].lock() == layer.image_data &&
6547 m_thumbnail_smoothing[slot] == layer.smoothing)
6548 return m_thumbnails[slot].get();
6549
6550 // Reuses the already-decoded, already-cached grayscale pixels (see decode_height_texture()'s
6551 // own cache in TextureDisplacement.cpp) - only the gray-to-RGBA expansion and GPU upload below are
6552 // new work. Rebuilt when the texture *or the smoothing* changes, so the fast/shaded preview - which
6553 // samples this GPU texture directly - reflects the current smoothing rather than the raw image.
6554 std::unique_ptr<GLTexture> texture = upload_height_thumbnail(decode_height_texture(layer));
6555 if (!texture)
6556 return nullptr;
6557
6558 m_thumbnails[slot] = std::move(texture);
6559 m_thumbnail_source[slot] = layer.image_data;
6560 m_thumbnail_smoothing[slot] = layer.smoothing;
6561 return m_thumbnails[slot].get();
6562}
std::array< std::weak_ptr< const std::vector< unsigned char > >, TEXTURE_DISPLACEMENT_MAX_LAYERS > m_thumbnail_source
Definition GLGizmoTextureDisplacement.hpp:1128
std::array< float, TEXTURE_DISPLACEMENT_MAX_LAYERS > m_thumbnail_smoothing
Definition GLGizmoTextureDisplacement.hpp:1130
std::array< std::unique_ptr< GLTexture >, TEXTURE_DISPLACEMENT_MAX_LAYERS > m_thumbnails
Definition GLGizmoTextureDisplacement.hpp:1127
@ lock
Definition GLGizmoEmboss.cpp:209

References Slic3r::decode_height_texture(), Slic3r::TextureDisplacementLayer::empty(), Slic3r::TextureDisplacementLayer::image_data, m_thumbnail_smoothing, m_thumbnail_source, m_thumbnails, Slic3r::TextureDisplacementLayer::slot, Slic3r::TextureDisplacementLayer::smoothing, and Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS.

Here is the call graph for this function:

◆ get_left_button_state_type()

EnforcerBlockerType Slic3r::GUI::GLGizmoTextureDisplacement::get_left_button_state_type ( ) const
inlineoverrideprotectedvirtual

Reimplemented from Slic3r::GUI::GLGizmoPainterBase.

bool m_erase_mode
Definition GLGizmoTextureDisplacement.hpp:517
@ NONE
Definition TriangleSelector.hpp:26

References Slic3r::ENFORCER, m_erase_mode, and Slic3r::NONE.

◆ get_library_texture()

const GLGizmoTextureDisplacement::LibraryTexture * Slic3r::GUI::GLGizmoTextureDisplacement::get_library_texture ( const std::string & path)
private
4290{
4291 if (auto it = m_library_textures.find(path); it != m_library_textures.end())
4292 return it->second.image_data ? &it->second : nullptr;
4293
4294 LibraryTexture entry;
4295 std::string error;
4296 entry.image_data = load_texture_image_data(path, error);
4297 if (entry.image_data) {
4298 TextureDisplacementLayer probe;
4299 probe.image_data = entry.image_data;
4300 entry.thumbnail = upload_height_thumbnail(decode_height_texture(probe, /* keep_decoded */ false));
4301 if (!entry.thumbnail)
4302 entry.image_data.reset(); // decoded to nothing usable - treat it as a failed load
4303 } else {
4304 BOOST_LOG_TRIVIAL(error) << "Texture displacement: could not load texture " << path << ": " << error;
4305 }
4306
4307 // Cached whether it loaded or not: a file that failed is remembered as unusable, so the picker
4308 // does not retry (and re-log) it on every frame it is on screen.
4309 const auto [pos, inserted] = m_library_textures.emplace(path, std::move(entry));
4310 return pos->second.image_data ? &pos->second : nullptr;
4311}
std::map< std::string, LibraryTexture > m_library_textures
Definition GLGizmoTextureDisplacement.hpp:1146
std::shared_ptr< std::vector< unsigned char > > image_data
Definition GLGizmoTextureDisplacement.hpp:497
std::shared_ptr< std::vector< unsigned char > > load_texture_image_data(const std::string &path, std::string &error)
Definition TextureLibrary.cpp:442
Vec3d pos(const Pt &p)
Definition ReprojectPointsOnMesh.hpp:16

References Slic3r::decode_height_texture(), Slic3r::GUI::GLGizmoTextureDisplacement::LibraryTexture::image_data, Slic3r::TextureDisplacementLayer::image_data, Slic3r::GUI::load_texture_image_data(), m_library_textures, and Slic3r::GUI::GLGizmoTextureDisplacement::LibraryTexture::thumbnail.

Referenced by add_texture_layer(), render_texture_library_popup(), and set_layer_texture().

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

◆ get_name()

std::string Slic3r::GUI::GLGizmoBase::get_name ( bool include_shortcut = true) const
inherited
545{
546 const std::string name = on_get_name();
547 return include_shortcut && m_shortcut.has_value() ? wxGetApp().shortcuts().with_key(name, *m_shortcut) : name;
548}
std::optional< Shortcut > m_shortcut
Definition GLGizmoBase.hpp:147

References m_shortcut, and on_get_name().

Referenced by Slic3r::GUI::GLGizmoAssembly::on_render_input_window(), Slic3r::GUI::GLGizmoBrimEars::on_render_input_window(), Slic3r::GUI::GLGizmoCut3D::on_render_input_window(), Slic3r::GUI::GLGizmoEmboss::on_render_input_window(), Slic3r::GUI::GLGizmoFdmSupports::on_render_input_window(), Slic3r::GUI::GLGizmoFuzzySkin::on_render_input_window(), Slic3r::GUI::GLGizmoMeasure::on_render_input_window(), Slic3r::GUI::GLGizmoMmuSegmentation::on_render_input_window(), Slic3r::GUI::GLGizmoSeam::on_render_input_window(), Slic3r::GUI::GLGizmoTextureDisplacement::on_render_input_window(), and Slic3r::GUI::GLGizmosManager::update_hover_state().

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

◆ get_painter_type()

PainterGizmoType Slic3r::GUI::GLGizmoTextureDisplacement::get_painter_type ( ) const
overrideprivatevirtual

Implements Slic3r::GUI::GLGizmoPainterBase.

656{
658}
@ TEXTURE_DISPLACEMENT
Definition GLGizmoPainterBase.hpp:41

References Slic3r::GUI::TEXTURE_DISPLACEMENT.

◆ get_projected_height_range()

std::vector< GLGizmoPainterBase::ProjectedHeightRange > Slic3r::GUI::GLGizmoPainterBase::get_projected_height_range ( const Vec2d & mouse_position,
double resolution,
const std::vector< const ModelVolume * > & part_volumes,
const std::vector< Transform3d > & trafo_matrices ) const
privateinherited
665{
666 std::vector<GLGizmoPainterBase::ProjectedHeightRange> hit_triangles_by_mesh;
667
668 const Camera& camera = wxGetApp().plater()->get_camera();
669
670 // In mesh_hit_points only the last item could have mesh_id == -1, any other items mustn't.
671 update_raycast_cache(mouse_position, camera, trafo_matrices);
672 if (m_rr.mesh_id == -1)
673 return hit_triangles_by_mesh;
674
675 ProjectedMousePosition mesh_hit_point = { m_rr.hit, m_rr.mesh_id, m_rr.facet };
676 float z_bot_world= (trafo_matrices[m_rr.mesh_id] * Vec3d(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2))).z();
677 float z_top_world = z_bot_world+ m_cursor_height;
678 hit_triangles_by_mesh.push_back({ z_bot_world, m_rr.mesh_id, size_t(m_rr.facet) });
679
680 const Selection& selection = m_parent.get_selection();
681 const ModelObject* mo = m_c->selection_info()->model_object();
682 const ModelInstance* mi = mo->instances[selection.get_instance_idx()];
683 const Transform3d instance_trafo = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
684 mi->get_assemble_transformation().get_matrix() :
685 mi->get_transformation().get_matrix();
686 const Transform3d instance_trafo_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
687 mi->get_assemble_transformation().get_matrix_no_offset() :
688 mi->get_transformation().get_matrix_no_offset();
689
690 for (int mesh_idx = 0; mesh_idx < part_volumes.size(); mesh_idx++) {
691 if (mesh_idx == m_rr.mesh_id)
692 continue;
693
694 const Transform3d& trafo = trafo_matrices[mesh_idx];
695 const indexed_triangle_set& its = part_volumes[mesh_idx]->mesh().its;
696
697 int first_hit_facet_idx = -1;
698 for (int facet_idx = 0; facet_idx < its.indices.size(); facet_idx++) {
699 stl_vertex v0 = its.vertices[its.indices[facet_idx].x()];
700 stl_vertex v1 = its.vertices[its.indices[facet_idx].y()];
701 stl_vertex v2 = its.vertices[its.indices[facet_idx].z()];
702
703 float v0_z = (trafo * Vec3d(v0(0), v0(1), v0(2))).z();
704 float v1_z = (trafo * Vec3d(v1(0), v1(1), v1(2))).z();
705 float v2_z = (trafo * Vec3d(v2(0), v2(1), v2(2))).z();
706 bool outside_range = (v0_z < z_bot_world&& v1_z < z_bot_world&& v2_z < z_bot_world) ||
707 (v0_z > z_top_world && v1_z > z_top_world && v2_z > z_top_world);
708 if (!outside_range) {
709 first_hit_facet_idx = facet_idx;
710 break;
711 }
712 }
713
714 if (first_hit_facet_idx != -1) {
715 hit_triangles_by_mesh.push_back({ z_bot_world, mesh_idx, (size_t)first_hit_facet_idx });
716 }
717 }
718
719 return hit_triangles_by_mesh;
720}
@ CanvasAssembleView
Definition GLCanvas3D.hpp:628
float m_cursor_height
Definition GLGizmoPainterBase.hpp:261
void update_raycast_cache(const Vec2d &mouse_position, const Camera &camera, const std::vector< Transform3d > &trafo_matrices) const
Definition GLGizmoPainterBase.cpp:1109
RaycastResult m_rr
Definition GLGizmoPainterBase.hpp:371
Definition GLGizmoPainterBase.hpp:283

References Slic3r::GUI::GLCanvas3D::CanvasAssembleView, Slic3r::ModelInstance::get_assemble_transformation(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::Geometry::Transformation::get_matrix(), Slic3r::Geometry::Transformation::get_matrix_no_offset(), Slic3r::ModelInstance::get_transformation(), Slic3r::ModelObject::instances, Slic3r::GUI::GLGizmoBase::m_c, m_cursor_height, Slic3r::GUI::GLGizmoBase::m_parent, m_rr, Slic3r::resolution(), and update_raycast_cache().

Referenced by gizmo_event().

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

◆ get_projected_mouse_positions()

std::vector< std::vector< GLGizmoPainterBase::ProjectedMousePosition > > Slic3r::GUI::GLGizmoPainterBase::get_projected_mouse_positions ( const Vec2d & mouse_position,
double resolution,
const std::vector< Transform3d > & trafo_matrices ) const
privateinherited
542{
543 // List of mouse positions that will be used as seeds for painting.
544 std::vector<Vec2d> mouse_positions{mouse_position};
545 if (m_last_mouse_click != Vec2d::Zero()) {
546 // In case current mouse position is far from the last one,
547 // add several positions from between into the list, so there
548 // are no gaps in the painted region.
549 if (size_t patches_in_between = size_t((mouse_position - m_last_mouse_click).norm() / resolution); patches_in_between > 0) {
550 const Vec2d diff = (m_last_mouse_click - mouse_position) / (patches_in_between + 1);
551 for (size_t patch_idx = 1; patch_idx <= patches_in_between; ++patch_idx)
552 mouse_positions.emplace_back(mouse_position + patch_idx * diff);
553 mouse_positions.emplace_back(m_last_mouse_click);
554 }
555 }
556
557 const Camera &camera = wxGetApp().plater()->get_camera();
558 std::vector<ProjectedMousePosition> mesh_hit_points;
559 mesh_hit_points.reserve(mouse_positions.size());
560
561 // In mesh_hit_points only the last item could have mesh_id == -1, any other items mustn't.
562 for (const Vec2d &mp : mouse_positions) {
563 update_raycast_cache(mp, camera, trafo_matrices);
564 mesh_hit_points.push_back({m_rr.hit, m_rr.mesh_id, m_rr.facet});
565 if (m_rr.mesh_id == -1)
566 break;
567 }
568
569 // Divide mesh_hit_points into groups with the same mesh_idx. It may contain multiple groups with the same mesh_idx.
570 std::vector<std::vector<ProjectedMousePosition>> mesh_hit_points_by_mesh;
571 for (size_t prev_mesh_hit_point = 0, curr_mesh_hit_point = 0; curr_mesh_hit_point < mesh_hit_points.size(); ++curr_mesh_hit_point) {
572 size_t next_mesh_hit_point = curr_mesh_hit_point + 1;
573 if (next_mesh_hit_point >= mesh_hit_points.size() || mesh_hit_points[curr_mesh_hit_point].mesh_idx != mesh_hit_points[next_mesh_hit_point].mesh_idx) {
574 mesh_hit_points_by_mesh.emplace_back();
575 mesh_hit_points_by_mesh.back().insert(mesh_hit_points_by_mesh.back().end(), mesh_hit_points.begin() + int(prev_mesh_hit_point), mesh_hit_points.begin() + int(next_mesh_hit_point));
576 prev_mesh_hit_point = next_mesh_hit_point;
577 }
578 }
579
580 auto on_same_facet = [](std::vector<ProjectedMousePosition> &hit_points) -> bool {
581 for (const ProjectedMousePosition &mesh_hit_point : hit_points)
582 if (mesh_hit_point.facet_idx != hit_points.front().facet_idx)
583 return false;
584 return true;
585 };
586
587 struct Plane
588 {
589 Vec3d origin;
590 Vec3d first_axis;
591 Vec3d second_axis;
592 };
593 auto find_plane = [](std::vector<ProjectedMousePosition> &hit_points) -> std::optional<Plane> {
594 assert(hit_points.size() >= 3);
595 for (size_t third_idx = 2; third_idx < hit_points.size(); ++third_idx) {
596 const Vec3d &first_point = hit_points[third_idx - 2].mesh_hit.cast<double>();
597 const Vec3d &second_point = hit_points[third_idx - 1].mesh_hit.cast<double>();
598 const Vec3d &third_point = hit_points[third_idx].mesh_hit.cast<double>();
599
600 const Vec3d first_vec = first_point - second_point;
601 const Vec3d second_vec = third_point - second_point;
602
603 // If three points aren't collinear, then there exists only one plane going through all points.
604 if (first_vec.cross(second_vec).squaredNorm() > sqr(EPSILON)) {
605 const Vec3d first_axis_vec_n = first_vec.normalized();
606 // Make second_vec perpendicular to first_axis_vec_n using Gram–Schmidt orthogonalization process
607 const Vec3d second_axis_vec_n = (second_vec - (first_vec.dot(second_vec) / first_vec.dot(first_vec)) * first_vec).normalized();
608 return Plane{second_point, first_axis_vec_n, second_axis_vec_n};
609 }
610 }
611
612 return std::nullopt;
613 };
614
615 for(std::vector<ProjectedMousePosition> &hit_points : mesh_hit_points_by_mesh) {
616 assert(!hit_points.empty());
617 if (hit_points.back().mesh_idx == -1)
618 break;
619
620 if (hit_points.size() <= 2)
621 continue;
622
623 if (on_same_facet(hit_points)) {
624 hit_points = {hit_points.front(), hit_points.back()};
625 } else if (std::optional<Plane> plane = find_plane(hit_points); plane) {
626 Polyline polyline;
627 polyline.points.reserve(hit_points.size());
628 // Project hit_points into its plane to simplified them in the next step.
629 for (auto &hit_point : hit_points) {
630 const Vec3d &point = hit_point.mesh_hit.cast<double>();
631 const double x_cord = plane->first_axis.dot(point - plane->origin);
632 const double y_cord = plane->second_axis.dot(point - plane->origin);
633 polyline.points.emplace_back(scale_(x_cord), scale_(y_cord));
634 }
635
636 polyline.simplify(scale_(m_cursor_radius) / 10.);
637
638 const int mesh_idx = hit_points.front().mesh_idx;
639 std::vector<ProjectedMousePosition> new_hit_points;
640 new_hit_points.reserve(polyline.points.size());
641 // Project 2D simplified hit_points beck to 3D.
642 for (const Point &point : polyline.points) {
643 const double x_cord = unscale<double>(point.x());
644 const double y_cord = unscale<double>(point.y());
645 const Vec3d new_hit_point = plane->origin + x_cord * plane->first_axis + y_cord * plane->second_axis;
646 const int facet_idx = m_c->raycaster()->raycasters()[mesh_idx]->get_closest_facet(new_hit_point.cast<float>());
647 new_hit_points.push_back({new_hit_point.cast<float>(), mesh_idx, size_t(facet_idx)});
648 }
649
650 hit_points = new_hit_points;
651 } else {
652 hit_points = {hit_points.front(), hit_points.back()};
653 }
654 }
655
656 return mesh_hit_points_by_mesh;
657}
#define EPSILON
Definition ColorUtils.cpp:64
Vec2d m_last_mouse_click
Definition GLGizmoPainterBase.hpp:357
float m_cursor_radius
Definition GLGizmoPainterBase.hpp:259
#define scale_(val)
Definition libslic3r.h:106
zaa_minimize_perimeter_height resolution((ConfigOptionFloats, retraction_minimum_travel))((ConfigOptionBools
constexpr T sqr(T x)
Definition libslic3r.h:301
T unscale(Q v)
Definition libslic3r.h:138
Eigen::Matrix< double, 2, 1, Eigen::DontAlign > Vec2d
Definition Point.hpp:67
@ Plane
Definition CadDocument.hpp:26
Slic3r::Polygons diff(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:731
Vec2d mouse_position(const wxMouseEvent &mouse_event)
Extract position of mouse from mouse event.
Definition SurfaceDrag.cpp:502

References Slic3r::diff(), EPSILON, Slic3r::GUI::GLGizmoBase::m_c, m_cursor_radius, m_last_mouse_click, m_rr, Slic3r::Plane, Slic3r::MultiPoint::points, Slic3r::resolution(), scale_, Slic3r::Polyline::simplify(), Slic3r::sqr(), Slic3r::unscale(), and update_raycast_cache().

Referenced by gizmo_event().

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

◆ get_requirements()

CommonGizmosDataID Slic3r::GUI::GLGizmoBase::get_requirements ( ) const
inlineinherited
186{ return on_get_requirements(); }
virtual CommonGizmosDataID on_get_requirements() const
Definition GLGizmoBase.hpp:248

References on_get_requirements().

Here is the call graph for this function:

◆ get_right_button_state_type()

EnforcerBlockerType Slic3r::GUI::GLGizmoTextureDisplacement::get_right_button_state_type ( ) const
inlineoverrideprotectedvirtual

◆ get_sprite_id()

unsigned int Slic3r::GUI::GLGizmoBase::get_sprite_id ( ) const
inlineinherited
204{ return m_sprite_id; }
unsigned int m_sprite_id
Definition GLGizmoBase.hpp:149

References m_sprite_id.

Referenced by Slic3r::GUI::GLGizmosManager::do_render_overlay().

Here is the caller graph for this function:

◆ get_state()

EState Slic3r::GUI::GLGizmoBase::get_state ( ) const
inlineinherited

◆ get_tilt_up_direction()

Vec3f Slic3r::GUI::GLGizmoPainterBase::get_tilt_up_direction ( ) const
protectedinherited
112{
113 return build_plate_tilt_up_direction().cast<float>();
114}
Vec3d build_plate_tilt_up_direction()
Definition GUI_App.cpp:10519

References Slic3r::GUI::build_plate_tilt_up_direction().

Referenced by gizmo_event(), Slic3r::GUI::GLGizmoTextureDisplacement::render_painted_colors(), and render_triangles().

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

◆ get_tooltip()

virtual std::string Slic3r::GUI::GLGizmoBase::get_tooltip ( ) const
inlinevirtualinherited

Mouse tooltip text.

Returns
Text to be visible in mouse tooltip

Reimplemented in Slic3r::GUI::GLGizmoCut3D, Slic3r::GUI::GLGizmoMove3D, Slic3r::GUI::GLGizmoRotate3D, Slic3r::GUI::GLGizmoRotate, and Slic3r::GUI::GLGizmoScale3D.

222{ return ""; }

Referenced by Slic3r::GUI::GLGizmosManager::get_tooltip().

Here is the caller graph for this function:

◆ gizmo_event()

bool Slic3r::GUI::GLGizmoPainterBase::gizmo_event ( SLAGizmoEventType action,
const Vec2d & mouse_position,
bool shift_down,
bool alt_down,
bool control_down )
virtualinherited
727{
728 Vec2d _mouse_position = mouse_position;
731 if (control_down) {
732 //BBS
736 m_parent.set_as_dirty();
737 return true;
738 }
739
743 m_parent.set_as_dirty();
744 return true;
745 }
746
750 m_parent.set_as_dirty();
751 if (m_rr.mesh_id != -1) {
752 const Selection &selection = m_parent.get_selection();
753 const ModelObject *mo = m_c->selection_info()->model_object();
754 const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
755 const Transform3d trafo_matrix_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
756 mi->get_assemble_transformation().get_matrix_no_offset() * mo->volumes[m_rr.mesh_id]->get_matrix_no_offset() :
757 mi->get_transformation().get_matrix_no_offset() * mo->volumes[m_rr.mesh_id]->get_matrix_no_offset();
758 const Transform3d trafo_matrix = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
759 mi->get_assemble_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix() :
760 mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix();
761 m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, this->get_clipping_plane_in_volume_coordinates(trafo_matrix), m_smart_fill_angle,
762 m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, get_tilt_up_direction(), true);
763 m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
764 m_seed_fill_last_mesh_id = m_rr.mesh_id;
765 }
766 return true;
767 }
768
772 std::min(TriangleSelectorPatch::gap_area + TriangleSelectorPatch::GapAreaStep, TriangleSelectorPatch::GapAreaMax);
773 m_parent.set_as_dirty();
774 return true;
775 }
776 }
777 }
778
779 if (action == SLAGizmoEventType::LeftDown
782
783 if (m_triangle_selectors.empty())
784 return false;
785
787 // BBS
788 if (action == SLAGizmoEventType::Dragging) {
790 return false;
791 }
792 else {
794 return false;
795 }
796
797 if (! shift_down) {
798 if (action == SLAGizmoEventType::Dragging)
800 else
802 }
803
804 const Camera &camera = wxGetApp().plater()->get_camera();
805 const Selection &selection = m_parent.get_selection();
806 const ModelObject *mo = m_c->selection_info()->model_object();
807 const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
808 Transform3d instance_trafo = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
809 mi->get_assemble_transformation().get_matrix() :
810 mi->get_transformation().get_matrix();
811 Transform3d instance_trafo_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
812 mi->get_assemble_transformation().get_matrix_no_offset() :
813 mi->get_transformation().get_matrix_no_offset();
814 std::vector<const ModelVolume*> part_volumes;
815
816 // Precalculate transformations of individual meshes.
817 std::vector<Transform3d> trafo_matrices;
818 std::vector<Transform3d> trafo_matrices_not_translate;
819 for (const ModelVolume *mv : mo->volumes)
820 if (mv->is_model_part()) {
821 if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
822 Transform3d temp = instance_trafo * mv->get_matrix();
823 temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
824 trafo_matrices.emplace_back(temp);
825 }
826 else {
827 trafo_matrices.emplace_back(instance_trafo* mv->get_matrix());
828 }
829 trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix_no_offset());
830 part_volumes.push_back(mv);
831 }
832
833 // BBS
835 {
836 std::vector<ProjectedHeightRange> projected_height_range_by_mesh = get_projected_height_range(_mouse_position, 1., part_volumes, trafo_matrices);
837 m_last_mouse_click = Vec2d::Zero();
838
839 for (int i = 0; i < projected_height_range_by_mesh.size(); i++) {
840 const ProjectedHeightRange& phr = projected_height_range_by_mesh[i];
841 int mesh_idx = phr.mesh_idx;
842
843 // The mouse button click detection is enabled when there is a valid hit.
844 // Missing the object entirely
845 // shall not capture the mouse.
846 const bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None);
847 if (mesh_idx != -1 && m_button_down == Button::None)
849
850 const Transform3d& trafo_matrix = trafo_matrices[mesh_idx];
851 const Transform3d& trafo_matrix_not_translate = trafo_matrices_not_translate[mesh_idx];
852
853 // Calculate direction from camera to the hit (in mesh coords):
854 Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
855 const TriangleSelector::ClippingPlane& clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
856
857 std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::SinglePointCursor::cursor_factory(phr.z_world,
858 camera_pos, m_cursor_height, trafo_matrix, clp);
859 m_triangle_selectors[mesh_idx]->select_patch(int(phr.first_facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate,
861
862 m_triangle_selectors[mesh_idx]->request_update_render_data(true);
863 m_last_mouse_click = _mouse_position;
864 }
865
866 return true;
867 }
868
871 _mouse_position.x() = m_last_mouse_click.x();
872 else if(m_horizontal_only)
873 _mouse_position.y() = m_last_mouse_click.y();
874 }
875
876 std::vector<std::vector<ProjectedMousePosition>> projected_mouse_positions_by_mesh = get_projected_mouse_positions(_mouse_position, 1., trafo_matrices);
877 m_last_mouse_click = Vec2d::Zero(); // only actual hits should be saved
878
879 for (const std::vector<ProjectedMousePosition> &projected_mouse_positions : projected_mouse_positions_by_mesh) {
880 assert(!projected_mouse_positions.empty());
881 const int mesh_idx = projected_mouse_positions.front().mesh_idx;
882 const bool dragging_while_painting = (action == SLAGizmoEventType::Dragging && m_button_down != Button::None);
883
884 // The mouse button click detection is enabled when there is a valid hit.
885 // Missing the object entirely
886 // shall not capture the mouse.
887 if (mesh_idx != -1)
890
891 // In case we have no valid hit, we can return. The event will be stopped when
892 // dragging while painting (to prevent scene rotations and moving the object)
893 if (mesh_idx == -1)
894 return dragging_while_painting;
895
896 const Transform3d &trafo_matrix = trafo_matrices[mesh_idx];
897 const Transform3d &trafo_matrix_not_translate = trafo_matrices_not_translate[mesh_idx];
898
899 // Calculate direction from camera to the hit (in mesh coords):
900 Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
901
902 assert(mesh_idx < int(m_triangle_selectors.size()));
903 const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
905 for(const ProjectedMousePosition &projected_mouse_position : projected_mouse_positions) {
906 assert(projected_mouse_position.mesh_idx == mesh_idx);
907 const Vec3f mesh_hit = projected_mouse_position.mesh_hit;
908 const int facet_idx = int(projected_mouse_position.facet_idx);
909 m_triangle_selectors[mesh_idx]->seed_fill_apply_on_triangles(new_state);
911 m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle,
914 // BBS: add infill_angle parameter
915 m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, -1.f, false, true);
917 // BBS: add infill_angle parameter
918 m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, m_smart_fill_angle, true, true);
919
921 }
922 } else if (m_tool_type == ToolType::BRUSH) {
924
925 if (projected_mouse_positions.size() == 1) {
926 const ProjectedMousePosition &first_position = projected_mouse_positions.front();
927 std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::SinglePointCursor::cursor_factory(first_position.mesh_hit,
928 camera_pos, m_cursor_radius,
929 m_cursor_type, trafo_matrix, clp);
930 m_triangle_selectors[mesh_idx]->select_patch(int(first_position.facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate,
932 } else {
933 for (auto first_position_it = projected_mouse_positions.cbegin(); first_position_it != projected_mouse_positions.cend() - 1; ++first_position_it) {
934 auto second_position_it = first_position_it + 1;
935 std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::DoublePointCursor::cursor_factory(first_position_it->mesh_hit, second_position_it->mesh_hit, camera_pos, m_cursor_radius, m_cursor_type, trafo_matrix, clp);
936 m_triangle_selectors[mesh_idx]->select_patch(int(first_position_it->facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate, m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, get_tilt_up_direction());
937 }
938 }
939 }
940
941 m_triangle_selectors[mesh_idx]->request_update_render_data(true);
942
943 m_last_mouse_click = _mouse_position;
944 }
945
946 return true;
947 }
948
950 if (m_triangle_selectors.empty())
951 return false;
952
953 const Camera &camera = wxGetApp().plater()->get_camera();
954 const Selection &selection = m_parent.get_selection();
955 const ModelObject *mo = m_c->selection_info()->model_object();
956 const ModelInstance *mi = mo->instances[selection.get_instance_idx()];
957 const Transform3d instance_trafo = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
958 mi->get_assemble_transformation().get_matrix() :
959 mi->get_transformation().get_matrix();
960 const Transform3d instance_trafo_not_translate = m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView ?
961 mi->get_assemble_transformation().get_matrix_no_offset() :
962 mi->get_transformation().get_matrix_no_offset();
963
964 // Precalculate transformations of individual meshes.
965 std::vector<Transform3d> trafo_matrices;
966 std::vector<Transform3d> trafo_matrices_not_translate;
967 for (const ModelVolume *mv : mo->volumes)
968 if (mv->is_model_part()) {
969 if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
970 Transform3d temp = instance_trafo * mv->get_matrix();
971 temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
972 trafo_matrices.emplace_back(temp);
973 }
974 else {
975 trafo_matrices.emplace_back(instance_trafo * mv->get_matrix());
976 }
977 trafo_matrices_not_translate.emplace_back(instance_trafo_not_translate * mv->get_matrix_no_offset());
978 }
979
980 // Now "click" into all the prepared points and spill paint around them.
981 update_raycast_cache(_mouse_position, camera, trafo_matrices);
982
983 auto seed_fill_unselect_all = [this]() {
984 for (auto &triangle_selector : m_triangle_selectors) {
985 triangle_selector->seed_fill_unselect_all_triangles();
986 triangle_selector->request_update_render_data();
987 }
988 };
989
990 if (m_rr.mesh_id == -1) {
991 // Clean selected by seed fill for all triangles in all meshes when a mouse isn't pointing on any mesh.
992 seed_fill_unselect_all();
994
995 // In case we have no valid hit, we can return.
996 return false;
997 }
998
999 // The mouse moved from one object's volume to another one. So it is needed to unselect all triangles selected by seed fill.
1000 if(m_rr.mesh_id != m_seed_fill_last_mesh_id)
1001 seed_fill_unselect_all();
1002
1003 const Transform3d &trafo_matrix = trafo_matrices[m_rr.mesh_id];
1004 const Transform3d &trafo_matrix_not_translate = trafo_matrices_not_translate[m_rr.mesh_id];
1005
1006 assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
1007 const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
1009 m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle,
1012 // BBS: add infill_angle parameter
1013 m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, -1.f, false);
1015 // BBS: add infill_angle parameter
1016 m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, m_smart_fill_angle, true);
1017 m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
1019 return true;
1020 }
1021
1022 if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::RightUp)
1024 // Take snapshot and update ModelVolume data.
1025 wxString action_name = this->handle_snapshot_action_name(shift_down, m_button_down);
1026 Plater::TakeSnapshot snapshot(wxGetApp().plater(), std::string(action_name.ToUTF8().data()), UndoRedo::SnapshotType::GizmoAction);
1028
1030 m_last_mouse_click = Vec2d::Zero();
1031 return true;
1032 }
1033
1034 return false;
1035}
static float explosion_ratio
Definition 3DScene.hpp:117
bool m_triangle_splitting_enabled
Definition GLGizmoPainterBase.hpp:297
virtual wxString handle_snapshot_action_name(bool shift_down, Button button_down) const =0
@ SMART_FILL
Definition GLGizmoPainterBase.hpp:277
@ BRUSH
Definition GLGizmoPainterBase.hpp:275
@ BUCKET_FILL
Definition GLGizmoPainterBase.hpp:276
@ GAP_FILL
Definition GLGizmoPainterBase.hpp:279
@ None
Definition GLGizmoPainterBase.hpp:323
@ Right
Definition GLGizmoPainterBase.hpp:325
@ Left
Definition GLGizmoPainterBase.hpp:324
std::vector< std::vector< ProjectedMousePosition > > get_projected_mouse_positions(const Vec2d &mouse_position, double resolution, const std::vector< Transform3d > &trafo_matrices) const
Definition GLGizmoPainterBase.cpp:541
bool m_paint_on_overhangs_only
Definition GLGizmoPainterBase.hpp:301
static constexpr float SmartFillAngleStep
Definition GLGizmoPainterBase.hpp:312
Vec3f get_tilt_up_direction() const
Definition GLGizmoPainterBase.cpp:111
virtual void update_model_object()=0
float m_highlight_by_angle_threshold_deg
Definition GLGizmoPainterBase.hpp:302
int m_seed_fill_last_mesh_id
Definition GLGizmoPainterBase.hpp:320
virtual const float get_cursor_radius_min() const
Definition GLGizmoPainterBase.hpp:217
virtual const float get_cursor_height_min() const
Definition GLGizmoPainterBase.hpp:222
std::vector< ProjectedHeightRange > get_projected_height_range(const Vec2d &mouse_position, double resolution, const std::vector< const ModelVolume * > &part_volumes, const std::vector< Transform3d > &trafo_matrices) const
Definition GLGizmoPainterBase.cpp:660
Button m_button_down
Definition GLGizmoPainterBase.hpp:359
std::vector< std::unique_ptr< TriangleSelectorGUI > > m_triangle_selectors
Definition GLGizmoPainterBase.hpp:270
TriangleSelector::CursorType m_cursor_type
Definition GLGizmoPainterBase.hpp:272
virtual const float get_cursor_height_step() const
Definition GLGizmoPainterBase.hpp:224
virtual const float get_cursor_height_max() const
Definition GLGizmoPainterBase.hpp:223
virtual EnforcerBlockerType get_left_button_state_type() const
Definition GLGizmoPainterBase.hpp:256
bool m_horizontal_only
Definition GLGizmoPainterBase.hpp:316
float m_smart_fill_angle
Definition GLGizmoPainterBase.hpp:299
static constexpr float SmartFillAngleMin
Definition GLGizmoPainterBase.hpp:310
virtual const float get_cursor_radius_max() const
Definition GLGizmoPainterBase.hpp:218
static constexpr float SmartFillAngleMax
Definition GLGizmoPainterBase.hpp:311
bool m_vertical_only
Definition GLGizmoPainterBase.hpp:315
TriangleSelector::ClippingPlane get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const
Definition GLGizmoPainterBase.cpp:1243
virtual const float get_cursor_radius_step() const
Definition GLGizmoPainterBase.hpp:219
ToolType m_tool_type
Definition GLGizmoPainterBase.hpp:298
virtual EnforcerBlockerType get_right_button_state_type() const
Definition GLGizmoPainterBase.hpp:257
Definition GLGizmoPainterBase.hpp:291
static constexpr float GapAreaMin
Definition GLGizmoPainterBase.hpp:131
static float gap_area
Definition GLGizmoPainterBase.hpp:136
static constexpr float GapAreaStep
Definition GLGizmoPainterBase.hpp:133
static std::unique_ptr< Cursor > cursor_factory(const Vec3f &first_center, const Vec3f &second_center, const Vec3f &camera_pos, const float cursor_radius, const CursorType cursor_type, const Transform3d &trafo_matrix, const ClippingPlane &clipping_plane)
Definition TriangleSelector.hpp:171
static std::unique_ptr< Cursor > cursor_factory(const Vec3f &center, const Vec3f &camera_pos, const float cursor_radius, const CursorType cursor_type, const Transform3d &trafo_matrix, const ClippingPlane &clipping_plane)
Definition TriangleSelector.hpp:143
@ POINTER
Definition TriangleSelector.hpp:84
@ HEIGHT_RANGE
Definition TriangleSelector.hpp:86
@ SPHERE
Definition TriangleSelector.hpp:83
@ CIRCLE
Definition TriangleSelector.hpp:82
@ LeftUp
Definition GLGizmosCommon.hpp:30
@ MouseWheelUp
Definition GLGizmosCommon.hpp:46
@ MouseWheelDown
Definition GLGizmosCommon.hpp:47
@ RightUp
Definition GLGizmosCommon.hpp:32
@ Dragging
Definition GLGizmosCommon.hpp:33
@ LeftDown
Definition GLGizmosCommon.hpp:29
@ RightDown
Definition GLGizmosCommon.hpp:31
@ Moving
Definition GLGizmosCommon.hpp:49
EnforcerBlockerType
Definition TriangleSelector.hpp:24

References BRUSH, BUCKET_FILL, Slic3r::GUI::GLCanvas3D::CanvasAssembleView, Slic3r::TriangleSelector::CIRCLE, Slic3r::TriangleSelector::DoublePointCursor::cursor_factory(), Slic3r::TriangleSelector::SinglePointCursor::cursor_factory(), Slic3r::GUI::Dragging, Slic3r::GLVolume::explosion_ratio, Slic3r::GUI::GLGizmoPainterBase::ProjectedMousePosition::facet_idx, Slic3r::GUI::GLGizmoPainterBase::ProjectedHeightRange::first_facet_idx, Slic3r::GUI::TriangleSelectorPatch::gap_area, GAP_FILL, Slic3r::GUI::TriangleSelectorPatch::GapAreaMax, Slic3r::GUI::TriangleSelectorPatch::GapAreaMin, Slic3r::GUI::TriangleSelectorPatch::GapAreaStep, Slic3r::ModelInstance::get_assemble_transformation(), get_clipping_plane_in_volume_coordinates(), get_cursor_height_max(), get_cursor_height_min(), get_cursor_height_step(), get_cursor_radius_max(), get_cursor_radius_min(), get_cursor_radius_step(), Slic3r::GUI::Selection::get_instance_idx(), get_left_button_state_type(), Slic3r::Geometry::Transformation::get_matrix(), Slic3r::ModelVolume::get_matrix(), Slic3r::Geometry::Transformation::get_matrix_no_offset(), Slic3r::ModelVolume::get_matrix_no_offset(), Slic3r::Geometry::Transformation::get_offset(), Slic3r::ModelInstance::get_offset_to_assembly(), Slic3r::GUI::Camera::get_position(), get_projected_height_range(), get_projected_mouse_positions(), get_right_button_state_type(), get_tilt_up_direction(), Slic3r::ModelInstance::get_transformation(), Slic3r::ModelVolume::get_transformation(), Slic3r::UndoRedo::GizmoAction, handle_snapshot_action_name(), Slic3r::TriangleSelector::HEIGHT_RANGE, Slic3r::ModelObject::instances, Slic3r::ModelVolume::is_model_part(), Left, Slic3r::GUI::LeftDown, Slic3r::GUI::LeftUp, m_button_down, Slic3r::GUI::GLGizmoBase::m_c, m_cursor_height, m_cursor_radius, m_cursor_type, m_highlight_by_angle_threshold_deg, m_horizontal_only, m_last_mouse_click, m_paint_on_overhangs_only, Slic3r::GUI::GLGizmoBase::m_parent, m_rr, m_seed_fill_last_mesh_id, m_smart_fill_angle, m_tool_type, m_triangle_selectors, m_triangle_splitting_enabled, m_vertical_only, Slic3r::GUI::GLGizmoPainterBase::ProjectedMousePosition::mesh_hit, Slic3r::GUI::GLGizmoPainterBase::ProjectedHeightRange::mesh_idx, Slic3r::GUI::MouseWheelDown, Slic3r::GUI::MouseWheelUp, Slic3r::GUI::Moving, Slic3r::NONE, None, Slic3r::GUI::plater(), Slic3r::TriangleSelector::POINTER, Right, Slic3r::GUI::RightDown, Slic3r::GUI::RightUp, SMART_FILL, SmartFillAngleMax, SmartFillAngleMin, SmartFillAngleStep, Slic3r::TriangleSelector::SPHERE, update_model_object(), update_raycast_cache(), Slic3r::ModelObject::volumes, and Slic3r::GUI::GLGizmoPainterBase::ProjectedHeightRange::z_world.

Referenced by Slic3r::GUI::GLGizmosManager::gizmo_event(), and on_mouse().

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

◆ GizmoImguiBegin()

◆ GizmoImguiEnd()

◆ GizmoImguiSetNextWIndowPos() [1/2]

void Slic3r::GUI::GLGizmoBase::GizmoImguiSetNextWIndowPos ( float & x,
float y,
float w,
float h,
int flag,
float pivot_x = 0.0f,
float pivot_y = 0.0f )
protectedinherited
384{
385 if (abs(w) > 0.01f) {
386 if (x + w > m_parent.get_canvas_size().get_width()) {
387 if (w > m_parent.get_canvas_size().get_width()) {
388 x = 0;
389 } else {
390 x = m_parent.get_canvas_size().get_width() - w;
391 }
392 }
393 }
394
395 m_imgui->set_next_window_pos(x, y, flag, pivot_x, pivot_y);
396}

References m_imgui, and m_parent.

◆ GizmoImguiSetNextWIndowPos() [2/2]

void Slic3r::GUI::GLGizmoBase::GizmoImguiSetNextWIndowPos ( float & x,
float y,
int flag,
float pivot_x = 0.0f,
float pivot_y = 0.0f )
protectedinherited

◆ group_preview_by_color()

indexed_triangle_set Slic3r::GUI::GLGizmoTextureDisplacement::group_preview_by_color ( const indexed_triangle_set & its,
const std::vector< uint8_t > & triangle_color,
const std::vector< ColorRGBA > & entry_colors,
std::vector< PreviewColorRun > & runs )
static
2433{
2434 runs.clear();
2435 if (triangle_color.size() != its.indices.size() || entry_colors.empty())
2436 return its;
2437
2438 indexed_triangle_set sorted;
2439 sorted.vertices = its.vertices;
2440 sorted.indices.reserve(its.indices.size());
2441 for (int want = 0; want <= int(entry_colors.size()); ++want) {
2442 const size_t first = sorted.indices.size();
2443 for (size_t i = 0; i < its.indices.size(); ++i)
2444 if (int(triangle_color[i]) == want)
2445 sorted.indices.push_back(its.indices[i]);
2446 if (sorted.indices.size() == first)
2447 continue;
2448 runs.push_back({ { first * 3, sorted.indices.size() * 3 },
2449 want == 0 ? GLVolume::NEUTRAL_COLOR : entry_colors[size_t(want - 1)] });
2450 }
2451 return sorted;
2452}
static ColorRGBA NEUTRAL_COLOR
Definition 3DScene.hpp:97

References Slic3r::GLVolume::NEUTRAL_COLOR.

Referenced by queue_preview_job().

Here is the caller graph for this function:

◆ handle_snapshot_action_name()

wxString Slic3r::GUI::GLGizmoTextureDisplacement::handle_snapshot_action_name ( bool shift_down,
ButtonButton button_down ) const
overrideprotectedvirtual

Implements Slic3r::GUI::GLGizmoPainterBase.

661{
662 // Right button does the opposite of the toggle; Shift always erases.
663 const bool erasing = shift_down || (m_erase_mode != (button_down == GLGizmoPainterBase::Button::Right));
664 return erasing ? _L("Erase texture displacement paint") : _L("Paint texture displacement");
665}
#define _L(s)
Definition OBJ.cpp:34

References _L, m_erase_mode, and Slic3r::GUI::GLGizmoPainterBase::Right.

◆ has_true_preview()

bool Slic3r::GUI::GLGizmoTextureDisplacement::has_true_preview ( ) const
inline
217{ return m_preview_glmodel.is_initialized() || !m_part_previews.empty(); }
GLModel m_preview_glmodel
Definition GLGizmoTextureDisplacement.hpp:904
std::vector< std::unique_ptr< PartPreview > > m_part_previews
Definition GLGizmoTextureDisplacement.hpp:211

References m_part_previews, and m_preview_glmodel.

Referenced by render_painter_gizmo().

Here is the caller graph for this function:

◆ import_custom_texture()

void Slic3r::GUI::GLGizmoTextureDisplacement::import_custom_texture ( TextureDisplacementLayer & layer)
private
4337{
4338 const wxString wildcard = _L("Images (*.png;*.jpg;*.jpeg;*.bmp)|*.png;*.jpg;*.jpeg;*.bmp");
4339 wxFileDialog dialog(nullptr, _L("Choose a texture image (height map)"), wxEmptyString, wxEmptyString, wildcard,
4340 wxFD_OPEN | wxFD_FILE_MUST_EXIST);
4341 if (dialog.ShowModal() != wxID_OK)
4342 return;
4343
4344 // Converts to the 8-bit grayscale PNG the bake code understands and copies it into the user's
4345 // own texture folder, so it stays available for later models (and survives an app update, which
4346 // rewrites the shipped folder wholesale).
4347 std::string error;
4348 const std::optional<TextureLibraryEntry> entry = import_texture_to_library(into_u8(dialog.GetPath()), error);
4349 if (!entry) {
4350 show_error(nullptr, error);
4351 return;
4352 }
4353 set_layer_texture(layer, *entry);
4354}
std::string into_u8(const wxString &str)
Definition GUI.cpp:549
std::optional< TextureLibraryEntry > import_texture_to_library(const std::string &source_path, std::string &error)
Definition TextureLibrary.cpp:427

References _L, Slic3r::GUI::import_texture_to_library(), Slic3r::GUI::into_u8(), set_layer_texture(), and Slic3r::GUI::show_error().

Referenced by render_texture_library_popup().

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

◆ init()

bool Slic3r::GUI::GLGizmoBase::init ( )
inlineinherited
168{ return on_init(); }
virtual bool on_init()=0

References on_init().

Here is the call graph for this function:

◆ invalidate_adjust_textures()

void Slic3r::GUI::GLGizmoTextureDisplacement::invalidate_adjust_textures ( )
private
4860{
4861 // The GPU copies of a layer's image - the list thumbnail, the preview shader's height and colour
4862 // textures - are keyed on the image_data pointer and the layer's Smoothing, and a live adjustment
4863 // changes neither: the pointer still names the same PNG. Rather than widen three cache keys for a
4864 // panel that is open a few seconds at a time, the panel drops the copies itself whenever it moves
4865 // the adjustment. Resetting the source is enough; the next get_layer_*() rebuilds from the decode,
4866 // which does see the adjustment.
4867 m_thumbnail_source.fill({});
4868 m_color_tex_source.reset();
4869 m_height_tex_source.reset();
4870}

References m_color_tex_source, m_height_tex_source, and m_thumbnail_source.

Referenced by close_image_adjust(), and render_image_adjust_window().

Here is the caller graph for this function:

◆ is_activable()

bool Slic3r::GUI::GLGizmoBase::is_activable ( ) const
inlineinherited
184{ return on_is_activable(); }
virtual bool on_is_activable() const
Definition GLGizmoBase.hpp:246

References on_is_activable().

Referenced by Slic3r::GUI::GLGizmosManager::activate_gizmo(), Slic3r::GUI::GLGizmosManager::do_render_overlay(), Slic3r::GUI::GLGizmoEmboss::init_create(), and Slic3r::GUI::GLGizmosManager::update_hover_state().

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

◆ is_dragging()

bool Slic3r::GUI::GLGizmoBase::is_dragging ( ) const
inlineinherited
209{ return m_dragging; }

References m_dragging.

Referenced by Slic3r::GUI::GLGizmoCut3D::gizmo_event(), Slic3r::GUI::GLGizmoEmboss::on_mouse_for_translate(), and Slic3r::GUI::GLGizmoSVG::on_mouse_for_translate().

Here is the caller graph for this function:

◆ is_in_editing_mode()

virtual bool Slic3r::GUI::GLGizmoBase::is_in_editing_mode ( ) const
inlinevirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoCut3D.

235{ return false; }

Referenced by Slic3r::GUI::GLGizmosManager::on_key().

Here is the caller graph for this function:

◆ is_mesh_point_clipped()

bool Slic3r::GUI::GLGizmoPainterBase::is_mesh_point_clipped ( const Vec3d & point,
const Transform3d & trafo ) const
privateinherited
528{
529 if (m_c->object_clipper()->get_position() == 0.)
530 return false;
531
532 auto sel_info = m_c->selection_info();
533 Vec3d transformed_point = trafo * point;
534 transformed_point(2) += sel_info->get_sla_shift();
535 return m_c->object_clipper()->get_clipping_plane()->is_point_clipped(transformed_point);
536}

References Slic3r::GUI::GLGizmoBase::m_c.

Referenced by update_raycast_cache().

Here is the caller graph for this function:

◆ is_painting()

bool Slic3r::GUI::GLGizmoPainterBase::is_painting ( ) const
inlineprotectedinherited
339{ return m_button_down != Button::None; }

References m_button_down, and None.

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

Here is the caller graph for this function:

◆ is_selectable()

bool Slic3r::GUI::GLGizmoBase::is_selectable ( ) const
inlineinherited
185{ return on_is_selectable(); }
virtual bool on_is_selectable() const
Definition GLGizmoBase.hpp:247

References on_is_selectable().

Here is the call graph for this function:

◆ is_selection_rectangle_dragging()

virtual bool Slic3r::GUI::GLGizmoBase::is_selection_rectangle_dragging ( ) const
inlinevirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoBrimEars, and Slic3r::GUI::GLGizmoCut3D.

236{ return false; }

◆ island_group_of()

int Slic3r::GUI::GLGizmoTextureDisplacement::island_group_of ( const std::vector< int > & groups,
int c )
staticprivate
1587{
1588 return (c >= 0 && size_t(c) < groups.size() && groups[size_t(c)] >= 0) ? groups[size_t(c)] : c;
1589}

Referenced by build_island_move_set().

Here is the caller graph for this function:

◆ island_highlight_selection()

const std::vector< int > * Slic3r::GUI::GLGizmoTextureDisplacement::island_highlight_selection ( )
private
2066{
2067 const TextureDisplacementLayer *al = active_layer();
2068 const UVEditorCanvas *uv_canvas = wxGetApp().plater()->get_uv_editor_canvas();
2069 if (m_show_uv_editor && al != nullptr && al->projection_method == TextureProjectionMethod::LSCM &&
2070 uv_canvas != nullptr && !m_uv_editor_unwrap.empty())
2071 return &uv_canvas->selected_islands();
2072 return nullptr;
2073}
bool m_show_uv_editor
Definition GLGizmoTextureDisplacement.hpp:1067

References active_layer(), Slic3r::LSCM, m_show_uv_editor, m_uv_editor_unwrap, Slic3r::TextureDisplacementLayer::projection_method, and Slic3r::GUI::UVEditorCanvas::selected_islands().

Referenced by follow_island_highlight(), and render_painter_gizmo().

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

◆ join_island_groups()

void Slic3r::GUI::GLGizmoTextureDisplacement::join_island_groups ( std::vector< int > & groups,
int a,
int b,
int chart_count )
staticprivate
1592{
1593 if (a < 0 || b < 0 || chart_count <= 0)
1594 return;
1595 // Materialise to a full explicit table first, so singletons (which were implicit) get a concrete id
1596 // that the relabel loop below can match on.
1597 if (int(groups.size()) < chart_count) {
1598 const size_t old = groups.size();
1599 groups.resize(size_t(chart_count));
1600 for (size_t i = old; i < groups.size(); ++i)
1601 groups[i] = int(i);
1602 }
1603 for (size_t i = 0; i < groups.size(); ++i)
1604 if (groups[i] < 0)
1605 groups[i] = int(i);
1606 const int ga = groups[size_t(a)], gb = groups[size_t(b)];
1607 if (ga == gb)
1608 return;
1609 const int g = std::min(ga, gb);
1610 for (int &x : groups)
1611 if (x == ga || x == gb)
1612 x = g;
1613}

Referenced by run_uv_command().

Here is the caller graph for this function:

◆ layer_projection_frame()

int Slic3r::GUI::GLGizmoTextureDisplacement::layer_projection_frame ( const indexed_triangle_set & local_patch,
const TextureDisplacementLayer & layer,
Vec3f & center,
Vec3f & axis ) const
private
1405{
1406 center = Vec3f::Zero();
1407 axis = Vec3f::UnitZ();
1408 const ModelVolume *mv = texture_volume();
1409 if (mv == nullptr || (layer.projection_method != TextureProjectionMethod::Cylindrical &&
1410 layer.projection_method != TextureProjectionMethod::Spherical))
1411 return 0;
1412 // The bake averages the *whole mesh's* vertex normals over the patch's corners, so this has to as
1413 // well: a patch-only average would sometimes quantize to a different world axis and wrap the
1414 // texture the other way round. Both meshes go through patch_in_world() first, which is the frame
1415 // the shaders' tex_pos lives in.
1416 Vec3f average_normal;
1419 center, axis, average_normal);
1420 return layer.projection_method == TextureProjectionMethod::Cylindrical ? 1 : 2;
1421}
std::vector< Vec3f > texture_displacement_vertex_normals(const indexed_triangle_set &its)
Definition TextureDisplacement.cpp:2499
void texture_displacement_patch_frame(const indexed_triangle_set &patch, const std::vector< Vec3f > &vertex_normals, Vec3f &center, Vec3f &axis, Vec3f &average_normal)
Definition TextureDisplacement.cpp:3549
void center(ExPolygonsWithIds &e)
Definition Emboss.cpp:1382
@ Spherical
Definition TextureDisplacement.hpp:76
@ Cylindrical
Definition TextureDisplacement.hpp:73

References Slic3r::center(), Slic3r::Cylindrical, Slic3r::TriangleMesh::its, Slic3r::ModelVolume::mesh(), patch_in_world(), Slic3r::TextureDisplacementLayer::projection_method, Slic3r::Spherical, Slic3r::texture_displacement_patch_frame(), Slic3r::texture_displacement_vertex_normals(), and texture_volume().

Referenced by rebuild_shaded_preview_mesh(), and rebuild_uvcheck_mesh().

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

◆ layer_texture_aspect()

float Slic3r::GUI::GLGizmoTextureDisplacement::layer_texture_aspect ( const TextureDisplacementLayer & layer)
staticprivate
1314{
1315 // decode_height_texture() is cached on the image_data allocation, so this is a hash lookup rather
1316 // than a PNG decode - cheap enough to call per rebuild.
1317 const DecodedHeightTexture tex = decode_height_texture(layer);
1318 return (tex.width > 0 && tex.height > 0) ? float(tex.width) / float(tex.height) : 1.f;
1319}

References Slic3r::decode_height_texture(), Slic3r::DecodedHeightTexture::height, and Slic3r::DecodedHeightTexture::width.

Referenced by adjust_handle_center(), compute_layer_corner_uvs(), compute_layer_vertex_uvs(), on_mouse_adjust_texture(), and render_uvcheck_mesh().

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

◆ layers_for_part()

std::vector< TextureDisplacementLayer > Slic3r::GUI::GLGizmoTextureDisplacement::layers_for_part ( const ModelVolume & part) const
private
3833{
3834 const ModelVolume *owner = texture_volume();
3835 if (owner == nullptr)
3836 return {};
3837 std::vector<TextureDisplacementLayer> out = owner->texture_displacement_layers;
3838 if (&part == owner)
3839 return out;
3840 for (TextureDisplacementLayer &layer : out) {
3841 layer.lscm_seam_edges.clear();
3842 layer.islands.clear();
3843 layer.island_groups.clear();
3844 layer.lscm_uv_overrides.clear();
3845 }
3846 return out;
3847}

References Slic3r::ModelVolume::texture_displacement_layers, and texture_volume().

Referenced by bake(), queue_prepare(), and queue_preview_job().

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

◆ load()

void Slic3r::GUI::GLGizmoBase::load ( cereal::BinaryInputArchive & ar)
inlineinherited
170{ m_state = On; on_load(ar); }
virtual void on_load(cereal::BinaryInputArchive &ar)
Definition GLGizmoBase.hpp:241

References m_state, On, and on_load().

Here is the call graph for this function:

◆ load_image_adjust_source()

bool Slic3r::GUI::GLGizmoTextureDisplacement::load_image_adjust_source ( const TextureDisplacementLayer & layer)
private
4815{
4816 if (layer.empty())
4817 return false;
4818
4819 // The *raw* image: neither the layer's Smoothing nor the panel's own preview_adjust, so what the
4820 // sliders act on is always the image as it is actually stored rather than the last preview of it.
4821 // Without clearing preview_adjust here the adjustment would compound on itself every reload.
4822 TextureDisplacementLayer raw = layer;
4823 raw.smoothing = 0.f;
4824 raw.preview_adjust = TextureAdjustments{};
4825
4826 DecodedHeightTexture decoded = decode_height_texture(raw);
4827 if (decoded.empty())
4828 return false;
4829
4830 m_adjust_source = std::move(decoded);
4832 m_adjust_source_image = layer.image_data.get();
4833 // The unadjusted image, so the box is filled now rather than one background pass later. Pairing it
4834 // with an identity `shown` is what makes update_image_adjust_preview() relaunch for the sliders'
4835 // current values against the new source instead of deciding it is already showing them.
4836 m_adjust_preview_shown = TextureAdjustments{};
4839 return true;
4840}
TextureAdjustments m_adjust_preview_shown
Definition GLGizmoTextureDisplacement.hpp:587
DecodedHeightTexture downscale_height_texture(const DecodedHeightTexture &src, int max_px)
Definition TextureDisplacement.cpp:507

References Slic3r::decode_height_texture(), Slic3r::downscale_height_texture(), Slic3r::DecodedHeightTexture::empty(), Slic3r::TextureDisplacementLayer::empty(), Slic3r::TextureDisplacementLayer::image_data, m_adjust_preview_shown, m_adjust_preview_source, m_adjust_preview_tex, m_adjust_preview_valid, m_adjust_source, m_adjust_source_image, Slic3r::TextureDisplacementLayer::preview_adjust, and Slic3r::TextureDisplacementLayer::smoothing.

Referenced by open_image_adjust(), and render_image_adjust_window().

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

◆ load_render_colors()

void Slic3r::GUI::GLGizmoBase::load_render_colors ( )
staticinherited
93{
99}
@ RenderCol_Grabber_Y
Definition GUI_Colors.hpp:27
@ RenderCol_Flatten_Plane
Definition GUI_Colors.hpp:29
@ RenderCol_Grabber_X
Definition GUI_Colors.hpp:26
@ RenderCol_Grabber_Z
Definition GUI_Colors.hpp:28
@ RenderCol_Flatten_Plane_Hover
Definition GUI_Colors.hpp:30
static ColorRGBA FLATTEN_COLOR
Definition GLGizmoBase.hpp:63
static std::array< ColorRGBA, 3 > AXES_COLOR
Definition GLGizmoBase.hpp:60
static ColorRGBA FLATTEN_HOVER_COLOR
Definition GLGizmoBase.hpp:64
static ImVec4 to_ImVec4(const ColorRGB &color)
Definition ImGuiWrapper.cpp:1053
static ImVec4 colors[RenderCol_Count]
Definition GUI_Colors.hpp:40

References AXES_COLOR, Slic3r::RenderColor::colors, FLATTEN_COLOR, FLATTEN_HOVER_COLOR, RenderCol_Flatten_Plane, RenderCol_Flatten_Plane_Hover, RenderCol_Grabber_X, RenderCol_Grabber_Y, RenderCol_Grabber_Z, and Slic3r::GUI::ImGuiWrapper::to_ImVec4().

Referenced by Slic3r::GUI::GLCanvas3D::init().

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

◆ make_palette()

std::vector< GLGizmoTextureDisplacement::PaletteEntry > Slic3r::GUI::GLGizmoTextureDisplacement::make_palette ( const std::vector< ColorRGBA > & filaments,
const std::vector< PaletteEntry > & mixes )
static
5986{
5987 std::vector<PaletteEntry> out;
5988 out.reserve(filaments.size() + mixes.size());
5989 for (int i = 0; i < int(filaments.size()); ++i)
5990 out.push_back({ Vec3f(filaments[size_t(i)].r(), filaments[size_t(i)].g(), filaments[size_t(i)].b()), i, i, 1, 1 });
5991 out.insert(out.end(), mixes.begin(), mixes.end());
5992 return out;
5993}

Referenced by cached_palette(), color_settings_for(), and palette_quantizers().

Here is the caller graph for this function:

◆ make_palette_quantizer()

ColorQuantizeFn Slic3r::GUI::GLGizmoTextureDisplacement::make_palette_quantizer ( const std::vector< PaletteEntry > & palette)
static
6020{
6021 if (palette.empty())
6022 return nullptr;
6023
6024 // Lab once per entry, not once per lookup.
6025 struct Lab { float l, a, b; };
6026 std::vector<Lab> palette_lab(palette.size());
6027 for (size_t i = 0; i < palette.size(); ++i) {
6028 const Vec3f lab = srgb_to_lab(palette[i].rgb);
6029 palette_lab[i] = { lab.x(), lab.y(), lab.z() };
6030 }
6031
6032 constexpr int E = PALETTE_LUT_EDGE;
6033 auto lut = std::make_shared<std::vector<uint8_t>>(size_t(E) * E * E, 0);
6034 tbb::parallel_for(tbb::blocked_range<int>(0, E), [&](const tbb::blocked_range<int> &range) {
6035 for (int r = range.begin(); r < range.end(); ++r)
6036 for (int g = 0; g < E; ++g)
6037 for (int b = 0; b < E; ++b) {
6038 // Cell centre, so the quantization error is symmetric across the cell.
6039 float l0, a0, b0;
6040 const Vec3f lab0 = srgb_to_lab(Vec3f((r + 0.5f) / E, (g + 0.5f) / E, (b + 0.5f) / E));
6041 l0 = lab0.x(); a0 = lab0.y(); b0 = lab0.z();
6042 int best = 0, best_pure = -1;
6043 float best_d = std::numeric_limits<float>::max(), best_pure_d = best_d;
6044 for (size_t i = 0; i < palette_lab.size(); ++i) {
6045 const float d = DeltaE00(l0, a0, b0, palette_lab[i].l, palette_lab[i].a, palette_lab[i].b);
6046 if (d < best_d) {
6047 best_d = d;
6048 best = int(i);
6049 }
6050 if (!palette[i].is_mix() && d < best_pure_d) {
6051 best_pure_d = d;
6052 best_pure = int(i);
6053 }
6054 }
6055 // A mix only where it clearly beats the nearest single filament, see PREFER_PURE_DE.
6056 if (best_pure >= 0 && palette[size_t(best)].is_mix() && best_pure_d - best_d < PREFER_PURE_DE)
6057 best = best_pure;
6058 (*lut)[(size_t(r) * E + size_t(g)) * E + size_t(b)] = uint8_t(best);
6059 }
6060 });
6061
6062 return [lut](const Vec3f &rgb) -> int {
6063 constexpr int E = PALETTE_LUT_EDGE;
6064 const int r = std::clamp(int(rgb.x() * E), 0, E - 1);
6065 const int g = std::clamp(int(rgb.y() * E), 0, E - 1);
6066 const int b = std::clamp(int(rgb.z() * E), 0, E - 1);
6067 return int((*lut)[(size_t(r) * E + size_t(g)) * E + size_t(b)]);
6068 };
6069}
float DeltaE00(float l1, float a1, float b1, float l2, float a2, float b2)
Definition ColorSpaceConvert.cpp:148
static Vec3f srgb_to_lab(const Vec3f &c)
Definition GLGizmoTextureDisplacement.cpp:377
constexpr int PALETTE_LUT_EDGE
Definition GLGizmoTextureDisplacement.cpp:320
auto range(Cont &&cont)
Definition libslic3r.h:405
@ E
Definition libslic3r.h:144

References DeltaE00(), Slic3r::E, and Slic3r::range().

Referenced by palette_quantizers(), prepare_mesh(), Slic3r::GUI::TextureDisplacementBakeJob::process(), and Slic3r::GUI::TextureDisplacementPreviewJob::process().

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

◆ mark_seam_path()

void Slic3r::GUI::GLGizmoTextureDisplacement::mark_seam_path ( int v_from,
int v_to )
private
1037{
1038 TextureDisplacementLayer *layer = active_layer();
1039 const ModelVolume *mv = texture_volume();
1040 if (layer == nullptr || mv == nullptr || v_from < 0 || v_to < 0 || v_from == v_to)
1041 return;
1042 const indexed_triangle_set &its = mv->mesh().its;
1043 const size_t n = its.vertices.size();
1044 if (size_t(v_from) >= n || size_t(v_to) >= n)
1045 return;
1046
1047 // Shortest path over the mesh's edge graph (Dijkstra, edge weight = length). Built on demand; one
1048 // pass per click is fine even on a dense mesh.
1049 std::vector<std::vector<std::pair<int, float>>> adj(n);
1050 for (const stl_triangle_vertex_indices &tri : its.indices)
1051 for (int i = 0; i < 3; ++i) {
1052 const int a = tri[i], b = tri[(i + 1) % 3];
1053 const float w = (its.vertices[size_t(a)] - its.vertices[size_t(b)]).norm();
1054 adj[size_t(a)].push_back({ b, w });
1055 adj[size_t(b)].push_back({ a, w });
1056 }
1057
1058 std::vector<float> dist(n, std::numeric_limits<float>::infinity());
1059 std::vector<int> prev(n, -1);
1060 using QN = std::pair<float, int>;
1061 std::priority_queue<QN, std::vector<QN>, std::greater<QN>> pq;
1062 dist[size_t(v_from)] = 0.f;
1063 pq.push({ 0.f, v_from });
1064 while (!pq.empty()) {
1065 const auto [d, u] = pq.top();
1066 pq.pop();
1067 if (d > dist[size_t(u)])
1068 continue;
1069 if (u == v_to)
1070 break;
1071 for (const auto &[w, ew] : adj[size_t(u)]) {
1072 const float nd = d + ew;
1073 if (nd < dist[size_t(w)]) {
1074 dist[size_t(w)] = nd;
1075 prev[size_t(w)] = u;
1076 pq.push({ nd, w });
1077 }
1078 }
1079 }
1080 if (prev[size_t(v_to)] < 0)
1081 return; // unreachable (disconnected components)
1082
1083 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Mark texture seam path"), UndoRedo::SnapshotType::GizmoAction);
1084 auto &seams = layer->lscm_seam_edges;
1085 for (int v = v_to; v != v_from && v >= 0; v = prev[size_t(v)]) {
1086 const int p = prev[size_t(v)];
1087 if (p < 0)
1088 break;
1089 const std::pair<int, int> e{ std::min(v, p), std::max(v, p) };
1090 if (std::find(seams.begin(), seams.end(), e) == seams.end())
1091 seams.push_back(e);
1092 }
1094}
T dist(const boost::polygon::point_data< T > &p1, const boost::polygon::point_data< T > &p2)
Definition Geometry.cpp:298

References _u8L, active_layer(), Slic3r::Geometry::dist(), Slic3r::UndoRedo::GizmoAction, Slic3r::TriangleMesh::its, Slic3r::TextureDisplacementLayer::lscm_seam_edges, Slic3r::ModelVolume::mesh(), Slic3r::GUI::plater(), rebuild_preview(), and texture_volume().

Referenced by on_mouse_seam().

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

◆ masks_after_subdivision()

TextureDisplacementFacetsData Slic3r::GUI::GLGizmoTextureDisplacement::masks_after_subdivision ( const TriangleMesh & new_mesh,
const std::vector< int > & source,
const std::array< LayerPaintMap, TEXTURE_DISPLACEMENT_MAX_LAYERS > & paint )
staticprivate
5603{
5604 // Children inherit their parent's source triangle, and subdivision only ever adds edge midpoints,
5605 // so every new triangle lies inside its source and on the same surface - which means the source's
5606 // painted *pieces* can be queried directly by point containment. That is what keeps a brush outline
5607 // smooth: rounding each source to wholly painted or not instead leaves a ragged fringe of isolated
5608 // triangles along any curved boundary, and the refined mesh then reproduces that fringe exactly
5609 // rather than hiding it.
5611 const indexed_triangle_set &its = new_mesh.its;
5612 for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot) {
5613 const LayerPaintMap &pm = paint[size_t(slot)];
5614 if (pm.empty())
5615 continue;
5616 TriangleSelector sel(new_mesh);
5617 for (size_t i = 0; i < source.size() && i < its.indices.size(); ++i) {
5618 const size_t S = size_t(source[i]);
5619 if (S >= pm.full.size())
5620 continue;
5621 bool painted = pm.full[S] != 0;
5622 if (!painted && pm.part_start[S] != pm.part_start[S + 1]) {
5623 const stl_triangle_vertex_indices &t = its.indices[i];
5624 const Vec3f centroid = (its.vertices[size_t(t[0])] + its.vertices[size_t(t[1])] +
5625 its.vertices[size_t(t[2])]) / 3.f;
5626 for (int k = pm.part_start[S]; k < pm.part_start[S + 1] && !painted; ++k)
5627 painted = point_in_triangle_coplanar(centroid, pm.part[size_t(k)]);
5628 }
5629 if (painted)
5630 sel.set_facet(int(i), EnforcerBlockerType::ENFORCER);
5631 }
5632 out[size_t(slot)] = sel.serialize();
5633 }
5634 return out;
5635}
bool point_in_triangle_coplanar(const Vec3f &p, const std::array< Vec3f, 3 > &t)
Definition GLGizmoTextureDisplacement.cpp:192

References Slic3r::GUI::GLGizmoTextureDisplacement::LayerPaintMap::empty(), Slic3r::ENFORCER, Slic3r::GUI::GLGizmoTextureDisplacement::LayerPaintMap::full, Slic3r::TriangleMesh::its, Slic3r::GUI::GLGizmoTextureDisplacement::LayerPaintMap::part, Slic3r::GUI::GLGizmoTextureDisplacement::LayerPaintMap::part_start, Slic3r::TriangleSelector::serialize(), Slic3r::TriangleSelector::set_facet(), and Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS.

Referenced by prepare_mesh().

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

◆ mesh_trafo_matrices()

std::vector< Transform3d > Slic3r::GUI::GLGizmoPainterBase::mesh_trafo_matrices ( ) const
privateinherited
186{
187 const ModelObject* mo = m_c->selection_info()->model_object();
188 const Selection& selection = m_parent.get_selection();
189 const ModelInstance* mi = mo->instances[selection.get_instance_idx()];
190
191 std::vector<Transform3d> trafo_matrices;
192 for (const ModelVolume* mv : mo->volumes) {
193 if (mv->is_model_part())
194 {
195 if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
196 Transform3d temp = mi->get_assemble_transformation().get_matrix() * mv->get_matrix();
197 temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
198 trafo_matrices.emplace_back(temp);
199 }
200 else {
201 trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix());
202 }
203 }
204 }
205 return trafo_matrices;
206}

References Slic3r::GUI::GLCanvas3D::CanvasAssembleView, Slic3r::GLVolume::explosion_ratio, Slic3r::ModelInstance::get_assemble_transformation(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::Geometry::Transformation::get_matrix(), Slic3r::ModelVolume::get_matrix(), Slic3r::Geometry::Transformation::get_offset(), Slic3r::ModelInstance::get_offset_to_assembly(), Slic3r::ModelInstance::get_transformation(), Slic3r::ModelVolume::get_transformation(), Slic3r::ModelObject::instances, Slic3r::ModelVolume::is_model_part(), Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, and Slic3r::ModelObject::volumes.

Referenced by render_cursor(), and render_follows_cursor().

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

◆ mix_targets()

std::vector< GLGizmoTextureDisplacement::MixTarget > Slic3r::GUI::GLGizmoTextureDisplacement::mix_targets ( const std::vector< TextureDisplacementLayer > & layers)
static
5813{
5814 // Each layer's bins, weighted by their share of that layer's pixels.
5815 std::vector<std::vector<MixTarget>> per_layer;
5816 for (const TextureDisplacementLayer &layer : layers) {
5817 if (!layer.color_enabled || layer.empty() || analyze_texture_detail(layer).flat_colors)
5818 continue;
5819 TextureDisplacementLayer raw = layer;
5820 raw.smoothing = 0.f;
5821 const DecodedHeightTexture tex = decode_height_texture(raw);
5822 if (!tex.has_color())
5823 continue;
5824
5825 // 16 levels per channel, each bin keeping the mean of the colours that fell in it: a coarse grid
5826 // to gather on, without snapping every colour to a bin corner.
5827 struct Bin
5828 {
5829 double r = 0., g = 0., b = 0.;
5830 uint32_t n = 0;
5831 };
5832 std::vector<Bin> bins(size_t(16 * 16 * 16));
5833 // Past the budget, one pixel from each run of `stride`, at an offset jittered by a fixed-seed
5834 // generator. A fixed step would sample a lattice that a striped texture can line up with, so
5835 // that only one of its colours is ever seen; jittered, stripes of any period or orientation are
5836 // sampled in proportion, and the same image still always gives the same targets. The offset
5837 // comes from the generator's high bits: a power-of-two LCG's low bits repeat every 2, 4, 8...
5838 // steps, and the stride is a power of two for exactly the images large enough to need this.
5839 const size_t npx = size_t(tex.width) * size_t(tex.height);
5840 const size_t stride = std::max<size_t>(1, npx / MIX_TARGET_SAMPLES);
5841 uint64_t state = 0x9E3779B97F4A7C15ull;
5842 size_t sampled = 0;
5843 for (size_t start = 0; start < npx; start += stride) {
5844 state = state * 6364136223846793005ull + 1442695040888963407ull;
5845 const size_t i = start + size_t((uint64_t(uint32_t(state >> 32)) * uint64_t(stride)) >> 32);
5846 if (i >= npx)
5847 break;
5848 const uint8_t *px = &tex.rgb[i * 3];
5849 Bin &bin = bins[size_t(px[0] >> 4) * 256 + size_t(px[1] >> 4) * 16 + size_t(px[2] >> 4)];
5850 bin.r += px[0];
5851 bin.g += px[1];
5852 bin.b += px[2];
5853 ++bin.n;
5854 ++sampled;
5855 }
5856 std::vector<MixTarget> targets;
5857 for (const Bin &bin : bins)
5858 if (bin.n > 0) {
5859 const double inv = 1. / (255. * double(bin.n));
5860 targets.push_back({ srgb_to_lab(Vec3f(float(bin.r * inv), float(bin.g * inv), float(bin.b * inv))),
5861 float(double(bin.n) / double(sampled)) });
5862 }
5863 per_layer.push_back(std::move(targets));
5864 }
5865
5866 // The layers weigh the same and together 1, settled before the pruning below: what rank_mixes() sums
5867 // over the kept bins is then a mean over every pixel of the colouring layers, with the pixels of a
5868 // dropped bin counted as no better off.
5869 std::vector<MixTarget> out;
5870 for (std::vector<MixTarget> &targets : per_layer)
5871 for (MixTarget &t : targets) {
5872 t.weight /= float(per_layer.size());
5873 out.push_back(t);
5874 }
5875 if (out.size() > MIX_TARGET_BINS) {
5876 std::partial_sort(out.begin(), out.begin() + MIX_TARGET_BINS, out.end(),
5877 [](const MixTarget &l, const MixTarget &r) { return l.weight > r.weight; });
5878 out.resize(MIX_TARGET_BINS);
5879 }
5880 return out;
5881}
Definition GLGizmoTextureDisplacement.hpp:97
int64_t px(const Point &pt)
Definition ClipperUtils.cpp:207
TextureDetail analyze_texture_detail(const TextureDisplacementLayer &layer)
Definition TextureDisplacement.cpp:643

References Slic3r::analyze_texture_detail(), Slic3r::decode_height_texture(), Slic3r::DecodedHeightTexture::has_color(), Slic3r::DecodedHeightTexture::height, Slic3r::DecodedHeightTexture::rgb, Slic3r::TextureDisplacementLayer::smoothing, and Slic3r::DecodedHeightTexture::width.

Referenced by cached_palette().

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

◆ model_uv_corners()

std::vector< Vec2f > Slic3r::GUI::GLGizmoTextureDisplacement::model_uv_corners ( const indexed_triangle_set & local_patch,
const TextureDisplacementLayer & layer,
const std::vector< int > * patch_source ) const
private
1340{
1341 const ModelVolume *mv = texture_volume();
1342 if (layer.projection_method != TextureProjectionMethod::ModelUV || patch_source == nullptr || mv == nullptr ||
1343 !mv->has_texture_uv())
1344 return {};
1345 // The channel is indexed against the volume's *local* mesh, and a uv is a coordinate in the
1346 // texture rather than on the model, so unlike every projected method this never goes through
1347 // patch_in_world() - there is no millimetre measurement here for a world scale to change.
1348 std::vector<Vec2f> uv = compute_model_corner_uvs(mv->mesh().its, mv->texture_uv, local_patch, *patch_source);
1349 for (Vec2f &p : uv)
1350 // Already a uv: no tiling scale, no aspect correction - see apply_uv_transform().
1351 p = apply_uv_transform(p, layer, 1.f, /* normalized_uv */ true);
1352 return uv;
1353}
std::vector< Vec2f > compute_model_corner_uvs(const indexed_triangle_set &base_mesh, const std::vector< Vec2f > &model_uv, const indexed_triangle_set &patch, const std::vector< int > &patch_source)
Definition TextureDisplacement.cpp:2076

References Slic3r::apply_uv_transform(), Slic3r::compute_model_corner_uvs(), Slic3r::ModelVolume::has_texture_uv(), Slic3r::TriangleMesh::its, Slic3r::ModelVolume::mesh(), Slic3r::ModelUV, Slic3r::TextureDisplacementLayer::projection_method, Slic3r::ModelVolume::texture_uv, and texture_volume().

Referenced by compute_layer_corner_uvs(), and compute_layer_vertex_uvs().

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

◆ move_texture_layer()

void Slic3r::GUI::GLGizmoTextureDisplacement::move_texture_layer ( int slot,
int to_index )
private
5219{
5220 ModelVolume *mv = texture_volume();
5221 if (mv == nullptr)
5222 return;
5223
5224 std::vector<int> slots;
5225 for (const TextureDisplacementLayer &l : mv->texture_displacement_layers)
5226 slots.push_back(l.slot);
5227 std::sort(slots.begin(), slots.end());
5228 const auto it = std::find(slots.begin(), slots.end(), slot);
5229 if (it == slots.end())
5230 return;
5231 const int from = int(it - slots.begin());
5232 // An insertion index counts the moved layer's own position, which is gone once it is lifted out.
5233 const int to = std::clamp(to_index > from ? to_index - 1 : to_index, 0, int(slots.size()) - 1);
5234 if (to == from)
5235 return;
5236
5237 update_model_object(); // flush the active layer's strokes into its slot before slots are exchanged
5238 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reorder texture displacement layers"),
5240 // Walked one neighbour at a time, so every layer in between shifts by one and keeps its order.
5241 const int step = to > from ? 1 : -1;
5242 for (int i = from; i != to; i += step)
5243 swap_layer_slots(slots[size_t(i)], slots[size_t(i + step)]);
5244
5245 // The active layer's mask now sits in a different slot, so the selectors are reloaded from there.
5246 update_from_model_object(false); // also rebuilds the preview
5249 m_parent.set_as_dirty();
5250}
bool update_adjust_anchor()
Definition GLGizmoTextureDisplacement.cpp:3492
bool m_adjust_texture_mode
Definition GLGizmoTextureDisplacement.hpp:1208
void swap_layer_slots(int slot_a, int slot_b)
Definition GLGizmoTextureDisplacement.cpp:5179

References _u8L, Slic3r::UndoRedo::GizmoAction, m_adjust_texture_mode, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::GUI::plater(), Slic3r::TextureDisplacementLayer::slot, swap_layer_slots(), Slic3r::ModelVolume::texture_displacement_layers, texture_volume(), update_adjust_anchor(), update_from_model_object(), and update_model_object().

Here is the call graph for this function:

◆ on_change_color_mode()

virtual void Slic3r::GUI::GLGizmoBase::on_change_color_mode ( bool is_dark)
inlinevirtualinherited
static bool is_dark()
Definition TextureImportDialog.cpp:85
bool m_is_dark_mode
Definition GLGizmoBase.hpp:157

References is_dark(), and m_is_dark_mode.

Here is the call graph for this function:

◆ on_disable_grabber()

virtual void Slic3r::GUI::GLGizmoBase::on_disable_grabber ( unsigned int id)
inlineprotectedvirtualinherited

◆ on_dragging()

virtual void Slic3r::GUI::GLGizmoBase::on_dragging ( const UpdateData & data)
inlineprotectedvirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoBrimEars, Slic3r::GUI::GLGizmoCut3D, Slic3r::GUI::GLGizmoEmboss, Slic3r::GUI::GLGizmoMove3D, Slic3r::GUI::GLGizmoRotate3D, Slic3r::GUI::GLGizmoRotate, Slic3r::GUI::GLGizmoScale3D, and Slic3r::GUI::GLGizmoSVG.

255{}

Referenced by use_grabbers().

Here is the caller graph for this function:

◆ on_enable_grabber()

virtual void Slic3r::GUI::GLGizmoBase::on_enable_grabber ( unsigned int id)
inlineprotectedvirtualinherited

◆ on_get_name()

std::string Slic3r::GUI::GLGizmoTextureDisplacement::on_get_name ( ) const
overrideprotectedvirtual

Implements Slic3r::GUI::GLGizmoBase.

572{
573 return _u8L("Texture displacement");
574}

References _u8L.

◆ on_get_requirements()

CommonGizmosDataID Slic3r::GUI::GLGizmoPainterBase::on_get_requirements ( ) const
overrideprotectedvirtualinherited

Reimplemented from Slic3r::GUI::GLGizmoBase.

1185{
1186 return CommonGizmosDataID(
1191}
CommonGizmosDataID
Definition GLGizmosCommon.hpp:72
@ SelectionInfo
Definition GLGizmosCommon.hpp:74
@ Raycaster
Definition GLGizmosCommon.hpp:76
@ InstancesHider
Definition GLGizmosCommon.hpp:75
@ ObjectClipper
Definition GLGizmosCommon.hpp:77

References Slic3r::GUI::InstancesHider, Slic3r::GUI::ObjectClipper, Slic3r::GUI::Raycaster, and Slic3r::GUI::SelectionInfo.

◆ on_init()

bool Slic3r::GUI::GLGizmoTextureDisplacement::on_init ( )
overrideprivatevirtual

Implements Slic3r::GUI::GLGizmoBase.

515{
517 const wxString ctrl = GUI::shortkey_ctrl_prefix();
518 const wxString alt = GUI::shortkey_alt_prefix();
519 const wxString shift = GUI::shortkey_shift_prefix();
520
521
522 m_desc["cursor_size"] = _L("Brush size");
523 m_desc["circle"] = _L("Circle");
524 m_desc["sphere"] = _L("Sphere");
525 m_desc["remove_layer"] = _L("Remove");
526 m_desc["bake"] = _L_CONTEXT("Bake", "Texture Displacement");
527
528
529 m_desc["paint"] = _L("Paint");
530 m_desc["erase"] = _L("Erase");
531 m_desc["gap_area"] = _L("Gap area");
532 m_desc["smart_fill_angle"] = _L("Smart fill angle");
533 m_desc["toggle_wireframe"] = _L("Toggle Wireframe");
534
535 std::pair<wxString, wxString> paint_shortcut = {_L("Left mouse button"), m_desc["paint"]};
536 std::pair<wxString, wxString> erase_shortcut = {shift + _L("Left mouse button"), m_desc["erase"]};
537 std::pair<wxString, wxString> toggle_wireframe_shortcut = {alt + shift + _L_CONTEXT("Enter", "Keyboard Shortcut"), m_desc["toggle_wireframe"]};
538
540 paint_shortcut,
541 erase_shortcut,
542 {ctrl + _L("Mouse wheel"), m_desc["cursor_size"]},
543 toggle_wireframe_shortcut
544 };
545
547 paint_shortcut,
548 erase_shortcut,
549 {ctrl + _L("Mouse wheel"), m_desc["smart_fill_angle"]},
550 toggle_wireframe_shortcut
551 };
552
554 {ctrl + _L("Mouse wheel"), m_desc["gap_area"]},
555 toggle_wireframe_shortcut
556 };
557
558 // Both ways into the same input box on any slider in the panel: ImGui turns a slider into one when
559 // it is Ctrl+clicked or when it is given the keyboard focus, which is what the edit icon asks for.
560 m_desc["type_value"] = _L("Type an exact value into a slider");
562 {ctrl + _L("Left mouse button"), m_desc["type_value"]},
563 {_L("Edit icon at a slider's right end"), m_desc["type_value"]},
564 };
565 for (std::vector<std::pair<wxString, wxString>> *list : {&m_shortcuts_brush, &m_shortcuts_bucket_fill, &m_shortcuts_gap_fill})
566 list->insert(list->end(), m_shortcuts_sliders.begin(), m_shortcuts_sliders.end());
567
568 return true;
569}
std::map< std::string, wxString > m_desc
Definition GLGizmoTextureDisplacement.hpp:1228
std::vector< std::pair< wxString, wxString > > m_shortcuts_gap_fill
Definition GLGizmoTextureDisplacement.hpp:1235
std::vector< std::pair< wxString, wxString > > m_shortcuts_brush
Definition GLGizmoTextureDisplacement.hpp:1231
std::vector< std::pair< wxString, wxString > > m_shortcuts_sliders
Definition GLGizmoTextureDisplacement.hpp:1238
std::vector< std::pair< wxString, wxString > > m_shortcuts_bucket_fill
Definition GLGizmoTextureDisplacement.hpp:1233
const std::string & shortkey_shift_prefix()
Definition GUI.cpp:109
@ GizmoDisplacement
Definition Shortcuts.hpp:41
const std::string & shortkey_alt_prefix()
Definition GUI.cpp:127
const std::string & shortkey_ctrl_prefix()
Definition GUI.cpp:115
#define _L_CONTEXT(s, ctx)
Definition I18N.hpp:15

References _L, _L_CONTEXT, Slic3r::GUI::GizmoDisplacement, m_desc, Slic3r::GUI::GLGizmoBase::m_shortcut, m_shortcuts_brush, m_shortcuts_bucket_fill, m_shortcuts_gap_fill, m_shortcuts_sliders, Slic3r::GUI::shortkey_alt_prefix(), Slic3r::GUI::shortkey_ctrl_prefix(), and Slic3r::GUI::shortkey_shift_prefix().

Here is the call graph for this function:

◆ on_is_activable()

bool Slic3r::GUI::GLGizmoTextureDisplacement::on_is_activable ( ) const
overrideprotectedvirtual

Reimplemented from Slic3r::GUI::GLGizmoPainterBase.

577{
579}
bool on_is_activable() const override
Definition GLGizmoPainterBase.cpp:1157

References Slic3r::GUI::GLCanvas3D::CanvasAssembleView, Slic3r::GUI::GLGizmoBase::m_parent, and Slic3r::GUI::GLGizmoPainterBase::on_is_activable().

Here is the call graph for this function:

◆ on_is_selectable()

bool Slic3r::GUI::GLGizmoPainterBase::on_is_selectable ( ) const
overrideprotectedvirtualinherited

Reimplemented from Slic3r::GUI::GLGizmoBase.

1176{
1177 //BBS
1178 /*return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF
1179 && wxGetApp().get_mode() != comSimple );*/
1180 return (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptFFF);
1181}
@ ptFFF
Definition Config.hpp:231

References Slic3r::ptFFF.

◆ on_island_edited()

void Slic3r::GUI::GLGizmoTextureDisplacement::on_island_edited ( int island,
const Vec2f & offset_delta,
float rotation_delta,
float scale_factor,
bool finished )
private
3385{
3386 TextureDisplacementLayer *layer = active_layer();
3387 if (layer == nullptr || island < 0)
3388 return;
3389 if (size_t(island) >= layer->islands.size())
3390 layer->islands.resize(size_t(island) + 1);
3391
3392 // A pure translation (no rotation, no scale) is the only gesture that moves a whole group/selection
3393 // together; rotate and scale stay on the primary island alone. This split is what makes flagging a
3394 // group's worth of vertices on the GPU safe: the shared shader delta is a pure translation, so the
3395 // same offset is correct for every flagged island.
3396 const bool is_move = rotation_delta == 0.f && scale_factor == 1.f;
3397
3398 if (!m_island_drag_active) {
3399 // Taken before the first delta lands, so one undo reverts the whole drag rather than just
3400 // its final mouse-move.
3401 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Move texture island"), UndoRedo::SnapshotType::GizmoAction);
3402 m_island_drag_active = true;
3403 // Decide the moved set once, at drag start: the whole selection + join groups for a move, or
3404 // just the primary for a rotate/scale.
3405 m_island_move_set = is_move ? build_island_move_set(*layer, island) : std::vector<int>{ island };
3406 // Set up the GPU drag: bake the mesh once (via the dirty flag), which is also what flags the
3407 // moved islands' triangles. From then on the drag is a uniform update, no rebuild - see
3408 // render_shaded_preview_mesh().
3409 m_shaded_active_chart = island;
3410 m_shaded_island_delta = Eigen::Matrix<float, 2, 3>::Identity();
3412 }
3413
3414 // Apply the edit. A move goes to every island in the moved set (same offset -> they translate as
3415 // one); a rotate/scale goes only to the primary, about its own centroid.
3416 const std::vector<int> single{ island };
3417 const std::vector<int> &targets = (is_move && !m_island_move_set.empty()) ? m_island_move_set : single;
3418 for (int c : targets) {
3419 if (c < 0)
3420 continue;
3421 if (size_t(c) >= layer->islands.size())
3422 layer->islands.resize(size_t(c) + 1);
3423 TextureIsland &target = layer->islands[size_t(c)];
3424 target.offset += offset_delta;
3425 if (c == island) {
3426 target.rotation_deg += rotation_delta;
3427 // Guarded: a scale that reaches zero is unrecoverable (every subsequent factor multiplies it)
3428 // and would collapse the island to a point - exactly the failure this feature hit once already.
3429 target.scale = std::clamp(target.scale * scale_factor, 0.001f, 1000.f);
3430 }
3431 }
3432
3433 if (finished) {
3434 m_island_drag_active = false;
3436 m_shaded_active_face.clear();
3437 m_island_move_set.clear();
3438 m_shaded_island_delta = Eigen::Matrix<float, 2, 3>::Identity();
3439 rebuild_preview(); // the real displaced geometry moved: recompute it once, at the end
3440 } else {
3441 const std::vector<Eigen::Matrix<float, 2, 3>> xf = uv_editor_island_transforms(*layer);
3442 if (UVEditorCanvas *uv_canvas = wxGetApp().plater()->get_uv_editor_canvas()) {
3443 // Live feedback in the pane, and deliberately *just* the transforms: nothing about the
3444 // unwrap changed, so none of the vertex buffers need touching. This is what makes a drag
3445 // interactive on a patch with a million triangles.
3446 uv_canvas->set_island_transforms(xf);
3447 }
3448 // Move the island on the model live through the shader's island_delta uniform - no mesh
3449 // rebuild. delta = F_current * F_baked^-1 in final-uv space.
3450 // The one rebuild that bakes the flags is
3451 // scheduled at drag start above and consumed once per frame by render_painter_gizmo().
3452 if (m_use_shaded_preview && m_shaded_active_chart == island && size_t(island) < xf.size()) {
3453 Eigen::Matrix3f cur = Eigen::Matrix3f::Identity();
3454 cur.topRows<2>() = xf[size_t(island)];
3455 Eigen::Matrix3f bak = Eigen::Matrix3f::Identity();
3456 bak.topRows<2>() = m_shaded_baked_active_xf;
3457 m_shaded_island_delta = (cur * bak.inverse()).topRows<2>();
3458 m_parent.set_as_dirty();
3459 }
3460 }
3461}
std::vector< int > m_island_move_set
Definition GLGizmoTextureDisplacement.hpp:1003
bool m_island_drag_active
Definition GLGizmoTextureDisplacement.hpp:741
std::vector< int > build_island_move_set(const TextureDisplacementLayer &layer, int primary) const
Definition GLGizmoTextureDisplacement.cpp:1615
std::vector< Eigen::Matrix< float, 2, 3 > > uv_editor_island_transforms(const TextureDisplacementLayer &layer)
Definition GLGizmoTextureDisplacement.cpp:3349
bool m_shaded_preview_dirty
Definition GLGizmoTextureDisplacement.hpp:917
Eigen::Matrix< float, 2, 3 > m_shaded_baked_active_xf
Definition GLGizmoTextureDisplacement.hpp:993
Eigen::Matrix< float, 2, 3 > m_shaded_island_delta
Definition GLGizmoTextureDisplacement.hpp:994
int m_shaded_active_chart
Definition GLGizmoTextureDisplacement.hpp:991

References _u8L, active_layer(), build_island_move_set(), Slic3r::UndoRedo::GizmoAction, Slic3r::TextureDisplacementLayer::islands, m_island_drag_active, m_island_move_set, Slic3r::GUI::GLGizmoBase::m_parent, m_shaded_active_chart, m_shaded_active_face, m_shaded_baked_active_xf, m_shaded_island_delta, m_shaded_preview_dirty, m_use_shaded_preview, Slic3r::TextureIsland::offset, Slic3r::GUI::plater(), rebuild_preview(), Slic3r::TextureIsland::rotation_deg, Slic3r::TextureIsland::scale, and uv_editor_island_transforms().

Referenced by update_uv_editor().

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

◆ on_load()

void Slic3r::GUI::GLGizmoPainterBase::on_load ( cereal::BinaryInputArchive & ar)
overrideprotectedvirtualinherited

Reimplemented from Slic3r::GUI::GLGizmoBase.

1234{
1235 // We should update the gizmo from current ModelObject, but it is not
1236 // possible at this point. That would require having updated selection and
1237 // common gizmos data, which is not done at this point. Instead, save
1238 // a flag to do the update in set_painter_gizmo_data, which will be called
1239 // soon after.
1240 m_schedule_update = true;
1241}

References m_schedule_update.

◆ on_mouse()

bool Slic3r::GUI::GLGizmoTextureDisplacement::on_mouse ( const wxMouseEvent & mouse_event)
overridevirtual

Implement when want to process mouse events in gizmo Click, Right click, move, drag, ...

Parameters
mouse_eventKeep information about mouse click
Returns
Return True when use the information and don't want to propagate it otherwise False.

Reimplemented from Slic3r::GUI::GLGizmoPainterBase.

848{
850 return on_mouse_seam(mouse_event);
852 return on_mouse_adjust_texture(mouse_event);
853
854 const bool handled = GLGizmoPainterBase::on_mouse(mouse_event);
855 // A consumed drag/click is a paint (or erase) event: the base class has already updated the live
856 // TriangleSelector, but nothing is flushed to the model - and so nothing rebuilds - until the
857 // stroke ends. Mark the tint stale so it follows the brush from the first frame instead. Only the
858 // flag is set here; the rebuild is coalesced to once per drawn frame in render_painter_gizmo().
859 if (handled && (mouse_event.Dragging() || mouse_event.LeftDown() || mouse_event.RightDown() ||
860 mouse_event.LeftUp() || mouse_event.RightUp()))
862 return handled;
863}
bool on_mouse(const wxMouseEvent &mouse_event) override
Implement when want to process mouse events in gizmo Click, Right click, move, drag,...
Definition GLGizmoPainterBase.cpp:1037
bool m_seam_edit_mode
Definition GLGizmoTextureDisplacement.hpp:712
bool on_mouse_seam(const wxMouseEvent &mouse_event)
Definition GLGizmoTextureDisplacement.cpp:865
bool on_mouse_adjust_texture(const wxMouseEvent &mouse_event)
Definition GLGizmoTextureDisplacement.cpp:3661

References m_adjust_texture_mode, m_paint_overlay_dirty, m_seam_edit_mode, Slic3r::GUI::GLGizmoPainterBase::on_mouse(), on_mouse_adjust_texture(), on_mouse_seam(), and Slic3r::GUI::wxMouseEvent.

Here is the call graph for this function:

◆ on_mouse_adjust_texture()

bool Slic3r::GUI::GLGizmoTextureDisplacement::on_mouse_adjust_texture ( const wxMouseEvent & mouse_event)
private
3662{
3664 return false;
3665
3666 ModelVolume *mv = texture_volume();
3667 ModelObject *mo = m_c->selection_info()->model_object();
3668 TextureDisplacementLayer *layer = active_layer();
3669 if (mv == nullptr || mo == nullptr || layer == nullptr)
3670 return false;
3671
3672 const Selection &selection = m_parent.get_selection();
3673 const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
3674 const Camera &camera = wxGetApp().plater()->get_camera();
3675
3676 const float bbox_size = float(mv->mesh().bounding_box().size().norm());
3677 const float panel_half = bbox_size * 0.04f + 1.f;
3678 const float arrow_length = panel_half * 2.2f;
3679 // Where the handle is drawn (moves with the layer's offset) vs. the plane the drag is measured
3680 // against (fixed at the anchor). Keeping them apart is what stops the handle's own motion from
3681 // feeding back into the delta that produced it.
3682 const Vec3f handle_center_local = adjust_handle_center(*layer);
3683 const Vec3f drag_plane_local = adjust_plane_point();
3684 const Vec3d handle_center_world = trafo_matrix * handle_center_local.cast<double>();
3685 const Vec2d mouse_pos(mouse_event.GetX(), mouse_event.GetY());
3686
3687 Vec3f u_axis, v_axis;
3688 adjust_tangent_basis(u_axis, v_axis);
3689
3690 const Point handle_screen = CameraUtils::project(camera, handle_center_world);
3691 const Vec2d handle_screen_d(double(handle_screen.x()), double(handle_screen.y()));
3692
3693 // Point-to-segment distance in screen space, for the arrow shafts.
3694 auto dist_to_segment_px = [](const Vec2d &p, const Vec2d &a, const Vec2d &b) {
3695 const Vec2d ab = b - a;
3696 const double len2 = ab.squaredNorm();
3697 const double t = (len2 > 1e-9) ? std::clamp((p - a).dot(ab) / len2, 0.0, 1.0) : 0.0;
3698 return (p - (a + ab * t)).norm();
3699 };
3700
3701 if (mouse_event.LeftDown()) {
3702 const Vec3d u_tip_world = trafo_matrix * (handle_center_local + u_axis * arrow_length).cast<double>();
3703 const Vec3d v_tip_world = trafo_matrix * (handle_center_local + v_axis * arrow_length).cast<double>();
3704 const Point u_tip_screen = CameraUtils::project(camera, u_tip_world);
3705 const Point v_tip_screen = CameraUtils::project(camera, v_tip_world);
3706 const Vec2d u_tip_screen_d(double(u_tip_screen.x()), double(u_tip_screen.y()));
3707 const Vec2d v_tip_screen_d(double(v_tip_screen.x()), double(v_tip_screen.y()));
3708
3709 // Panel screen-space "radius", approximated from one corner (a loose circle around the
3710 // square is close enough for hit-testing purposes).
3711 const Vec3d panel_corner_world = trafo_matrix * (handle_center_local + (u_axis + v_axis) * panel_half).cast<double>();
3712 const Point panel_corner_screen = CameraUtils::project(camera, panel_corner_world);
3713 const double panel_screen_radius = (Vec2d(double(panel_corner_screen.x()), double(panel_corner_screen.y())) - handle_screen_d).norm();
3714
3715 constexpr double pick_tolerance_px = 8.0;
3716 const double dist_to_u = dist_to_segment_px(mouse_pos, handle_screen_d, u_tip_screen_d);
3717 const double dist_to_v = dist_to_segment_px(mouse_pos, handle_screen_d, v_tip_screen_d);
3718 const double dist_to_panel = (handle_screen_d - mouse_pos).norm();
3719
3720 // Arrows take priority over the panel (their tips extend past it), then the panel covers
3721 // the broader central area.
3722 if (dist_to_u <= pick_tolerance_px && dist_to_u <= dist_to_v)
3724 else if (dist_to_v <= pick_tolerance_px)
3726 else if (dist_to_panel <= panel_screen_radius)
3728 else {
3730 return false;
3731 }
3732
3733 m_adjust_drag_start_offset = layer->offset;
3734
3735 Vec3d world_hit;
3736 if (ray_plane_hit(camera, mouse_pos, trafo_matrix, drag_plane_local, m_adjust_anchor_normal, world_hit)) {
3737 const Vec3f local_hit = (trafo_matrix.inverse() * world_hit).cast<float>();
3739 }
3740 return true;
3741 }
3742
3743 if (mouse_event.Dragging() && m_adjust_drag_handle != AdjustHandle::None) {
3744 Vec3d world_hit;
3745 if (!ray_plane_hit(camera, mouse_pos, trafo_matrix, drag_plane_local, m_adjust_anchor_normal, world_hit))
3746 return true;
3747 const Vec3f local_hit = (trafo_matrix.inverse() * world_hit).cast<float>();
3748 const Vec2f current_planar = project_planar(local_hit, m_adjust_anchor_normal);
3749
3750 Vec2f delta_planar = current_planar - m_adjust_drag_start_planar;
3751 // project_planar()'s (x, y) axes are exactly u_axis/v_axis (see adjust_tangent_basis()),
3752 // so zeroing one component constrains the drag to only the other axis.
3754 delta_planar.y() = 0.f;
3756 delta_planar.x() = 0.f;
3757
3758 const float scale = (layer->tiling_scale > 1e-6f) ? (1.f / layer->tiling_scale) : 1.f;
3759 const Vec2f delta_scaled = delta_planar * scale;
3760 const float rad = layer->rotation_deg * float(M_PI) / 180.f;
3761 const float cs = std::cos(rad), sn = std::sin(rad);
3762 Vec2f delta_rotated(delta_scaled.x() * cs - delta_scaled.y() * sn, delta_scaled.x() * sn + delta_scaled.y() * cs);
3763 // ...and the same v scaling apply_uv_transform() applies for a non-square texture, so the
3764 // handle keeps tracking the cursor exactly instead of drifting on the v axis.
3765 delta_rotated.y() *= layer_texture_aspect(*layer);
3766 // Increasing `offset` shifts which texel is sampled at a fixed world position, which
3767 // visually slides the pattern the *opposite* way - subtracting is this session's
3768 // best-effort reasoning about the direction that feels like "dragging the texture",
3769 // unverified against an actual render (see header comment).
3770 layer->offset = m_adjust_drag_start_offset - delta_rotated;
3771
3773 m_parent.set_as_dirty();
3774 return true;
3775 }
3776
3777 if (mouse_event.LeftUp() && m_adjust_drag_handle != AdjustHandle::None) {
3780 m_preview_params_dirty = false;
3781 return true;
3782 }
3783
3784 return false;
3785}
static Points project(const Camera &camera, const std::vector< Vec3d > &points)
Project point throw camera to 2d coordinate into imgui window.
Definition CameraUtils.cpp:23
Vec3f adjust_handle_center(const TextureDisplacementLayer &layer) const
Definition GLGizmoTextureDisplacement.cpp:3525
AdjustHandle m_adjust_drag_handle
Definition GLGizmoTextureDisplacement.hpp:1216
Vec2f m_adjust_drag_start_planar
Definition GLGizmoTextureDisplacement.hpp:1221
@ AxisV
Definition GLGizmoTextureDisplacement.hpp:1215
@ None
Definition GLGizmoTextureDisplacement.hpp:1215
@ Pan
Definition GLGizmoTextureDisplacement.hpp:1215
@ AxisU
Definition GLGizmoTextureDisplacement.hpp:1215
bool m_adjust_anchor_valid
Definition GLGizmoTextureDisplacement.hpp:1209
Vec2f m_adjust_drag_start_offset
Definition GLGizmoTextureDisplacement.hpp:1217
bool ray_plane_hit(const Camera &camera, const Vec2d &mouse_pos, const Transform3d &trafo, const Vec3f &plane_point_local, const Vec3f &plane_normal_local, Vec3d &out_world_hit)
Definition GLGizmoTextureDisplacement.cpp:487
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
Vec2f project_planar(const Vec3f &position, const Vec3f &normal)
Definition TextureDisplacement.cpp:799
T dot(const boost::geometry::model::d2::point_xy< T > &v1, const boost::geometry::model::d2::point_xy< T > &v2)
Definition ExtrusionSimulator.cpp:154
auto cast(const BoundingBoxBase< Tin > &b)
Definition BoundingBox.hpp:299

References active_layer(), adjust_handle_center(), adjust_plane_point(), adjust_tangent_basis(), AxisU, AxisV, Slic3r::TriangleMesh::bounding_box(), Slic3r::cast(), Slic3r::dot(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::ModelObject::instances, layer_texture_aspect(), m_adjust_anchor_normal, m_adjust_anchor_valid, m_adjust_drag_handle, m_adjust_drag_start_offset, m_adjust_drag_start_planar, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, M_PI, m_preview_params_dirty, Slic3r::ModelVolume::mesh(), None, Slic3r::TextureDisplacementLayer::offset, Pan, Slic3r::GUI::CameraUtils::project(), Slic3r::project_planar(), rebuild_preview(), Slic3r::TextureDisplacementLayer::rotation_deg, scale(), Slic3r::BoundingBox3Base< PointType >::size(), texture_volume(), Slic3r::TextureDisplacementLayer::tiling_scale, and Slic3r::GUI::wxMouseEvent.

Referenced by on_mouse().

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

◆ on_mouse_seam()

bool Slic3r::GUI::GLGizmoTextureDisplacement::on_mouse_seam ( const wxMouseEvent & mouse_event)
private
866{
867 // Hold Ctrl to orbit/pan the camera while in seam mode, exactly as the base painter lets you do
868 // while painting: with Ctrl down we consume nothing, so the canvas gets the drag and moves the
869 // view. Without this, seam mode swallowed every left-drag and the camera couldn't be rotated.
870 if (mouse_event.CmdDown())
871 return false;
872
873 // Left-click marks/unmarks an edge; swallow the rest of the left-button stream so a drag doesn't
874 // paint, but let everything else through so the camera still orbits/pans/zooms normally.
875 if (mouse_event.LeftDown()) {
876 const Vec2d pos(mouse_event.GetX(), mouse_event.GetY());
877 if (m_seam_path_mode) {
878 // Two-click shortest-path seam: first click sets the start vertex, the next seams the whole
879 // path to it and becomes the new start (so a seam line chains click by click).
880 const int v = seam_vertex_at(pos);
881 if (v >= 0) {
882 if (m_seam_path_anchor < 0)
884 else {
887 }
889 m_parent.set_as_dirty();
890 }
891 } else {
892 toggle_seam_at(pos);
893 }
894 return true;
895 }
896 // Live hover: highlight what a click would pick, so the clickable target is obvious (the "I don't
897 // know how it works" the user hit). In normal mode that is an edge; in shortest-path mode it is a
898 // vertex. Don't swallow the motion - the camera still needs it.
899 if (mouse_event.Moving()) {
900 const Vec2d pos(mouse_event.GetX(), mouse_event.GetY());
901 if (m_seam_path_mode) {
902 const int v = seam_vertex_at(pos);
903 if (v != m_seam_hover_vertex) {
905 m_seam_hover_edge = { -1, -1 };
907 m_parent.set_as_dirty();
908 }
909 } else {
910 const std::pair<int, int> edge = seam_edge_at(pos);
911 if (edge != m_seam_hover_edge) {
912 m_seam_hover_edge = edge;
915 m_parent.set_as_dirty();
916 }
917 }
918 }
919 if (mouse_event.LeftUp() || (mouse_event.Dragging() && mouse_event.LeftIsDown()))
920 return true;
921 return false;
922}
int seam_vertex_at(const Vec2d &mouse_pos) const
Definition GLGizmoTextureDisplacement.cpp:1003
std::pair< int, int > m_seam_hover_edge
Definition GLGizmoTextureDisplacement.hpp:722
int m_seam_path_anchor
Definition GLGizmoTextureDisplacement.hpp:734
int m_seam_hover_vertex
Definition GLGizmoTextureDisplacement.hpp:725
void toggle_seam_at(const Vec2d &mouse_pos)
Definition GLGizmoTextureDisplacement.cpp:985
void rebuild_seam_anchor_overlay()
Definition GLGizmoTextureDisplacement.cpp:1096
bool m_seam_path_mode
Definition GLGizmoTextureDisplacement.hpp:733
void mark_seam_path(int v_from, int v_to)
Definition GLGizmoTextureDisplacement.cpp:1036
std::pair< int, int > seam_edge_at(const Vec2d &mouse_pos) const
Definition GLGizmoTextureDisplacement.cpp:941
void rebuild_seam_hover_overlay()
Definition GLGizmoTextureDisplacement.cpp:1124

References Slic3r::GUI::GLGizmoBase::m_parent, m_seam_hover_edge, m_seam_hover_vertex, m_seam_path_anchor, m_seam_path_mode, mark_seam_path(), rebuild_seam_anchor_overlay(), rebuild_seam_hover_overlay(), seam_edge_at(), seam_vertex_at(), toggle_seam_at(), and Slic3r::GUI::wxMouseEvent.

Referenced by on_mouse().

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

◆ on_opening()

void Slic3r::GUI::GLGizmoTextureDisplacement::on_opening ( )
inlineoverrideprivatevirtual

◆ on_register_raycasters_for_picking()

virtual void Slic3r::GUI::GLGizmoBase::on_register_raycasters_for_picking ( )
inlineprotectedvirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoBrimEars, Slic3r::GUI::GLGizmoCut3D, Slic3r::GUI::GLGizmoEmboss, Slic3r::GUI::GLGizmoFlatten, Slic3r::GUI::GLGizmoMove3D, Slic3r::GUI::GLGizmoRotate3D, Slic3r::GUI::GLGizmoScale3D, and Slic3r::GUI::GLGizmoSVG.

267{}

Referenced by register_raycasters_for_picking().

Here is the caller graph for this function:

◆ on_render()

void Slic3r::GUI::GLGizmoPainterBase::on_render ( )
inlineoverrideprivatevirtualinherited

Implements Slic3r::GUI::GLGizmoBase.

201{}

◆ on_render_input_window()

void Slic3r::GUI::GLGizmoTextureDisplacement::on_render_input_window ( float x,
float y,
float bottom_limit )
overrideprotectedvirtual

Reimplemented from Slic3r::GUI::GLGizmoBase.

7021{
7022 ModelObject *mo = m_c->selection_info()->model_object();
7023 if (!mo)
7024 return;
7025 ModelVolume *mv = texture_volume();
7026
7027 // The palette also follows project state nobody tells this gizmo about - the filaments and mixed
7028 // slots in the sidebar - so it is checked once a frame, and a change re-runs the previews.
7032
7033 float scale = m_parent.get_scale();
7034 #ifdef WIN32
7035 int dpi = get_dpi_for_window(wxGetApp().GetTopWindow());
7036 scale *= (float) dpi / (float) DPI_DEFAULT;
7037 #endif // WIN32
7038
7039 const float approx_height = m_imgui->scaled(24.f);
7040 y = std::min(y, bottom_limit - approx_height);
7041
7042 // Docked (the default) the panel is pinned to the right edge of the 3D canvas and cannot be
7043 // moved. Deliberately *not* next to the gizmo toolbar, which is where `x` points and where every
7044 // other gizmo's window goes: this panel is far taller than those (layer stack plus the whole
7045 // per-layer control set), so at the toolbar it sits right on top of the part of the model being
7046 // painted. Pinning it to the canvas edge also parks it against the UV editor, since that pane is
7047 // docked on the right and the canvas therefore ends exactly at the pane's left edge - so the
7048 // panel follows the pane in and out instead of being clipped by it.
7049 //
7050 // Undocked it becomes an ordinary floating window: a title bar to drag it by, and no forced
7051 // position - the position is deliberately not seeded on undock, so the window stays exactly
7052 // where it already was and the user just gains the ability to move it from there.
7053 ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse;
7054 if (!m_undocked) {
7055 flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar;
7056 // Right-aligned (pivot 1), so the width the panel auto-resized to last frame does not need to
7057 // be known here. Width 0 skips GizmoImguiSetNextWIndowPos()'s own left-aligned fit-to-canvas
7058 // clamp, which would push the window back off the edge it is being pinned to.
7059 float right = float(m_parent.get_canvas_size().get_width()) - m_imgui->scaled(0.5f);
7060 GizmoImguiSetNextWIndowPos(right, y, 0.f, 0.f, ImGuiCond_Always, 1.0f, 0.0f);
7061 }
7062
7064 GizmoImguiBegin(get_name(), flags);
7065 // Where this frame's tooltips place themselves (see panel_tooltip()). The size is last frame's -
7066 // this window auto-resizes, so its height is only known once its content has been laid out - which
7067 // is close enough for a tooltip that needs a frame of hovering to appear in the first place.
7068 m_tooltip_anchor_min = ImGui::GetWindowPos();
7069 m_tooltip_anchor_max = ImVec2(m_tooltip_anchor_min.x + ImGui::GetWindowSize().x,
7070 m_tooltip_anchor_min.y + ImGui::GetWindowSize().y);
7072 process_uv_commands(); // clicks from the UV editor pane, run here where the GL context is current
7073 // After the pane's commands, which can close it or change the view, and before the View row, so the row
7074 // lights the view this frame ends up in and a click on it has the last word.
7076 const float previous_body_h = m_panel_body_h;
7077 const float previous_footer_h = m_panel_footer_h;
7078
7079 // Pinned every frame while Standard is active, so what Preview shows is always what Bake will do.
7082
7083 // Layout follows the Option C design: a fixed-width panel, a header pinned at the top, a body that
7084 // scrolls as one, and a footer with Bake pinned at the bottom. Rows are laid out against panel_w and
7085 // against the body child's own fixed width - never against this window's width, which under
7086 // AlwaysAutoResize is derived from its content and would feed back into it.
7087 const bool dark = wxGetApp().dark_mode();
7088 const bool busy = m_bake_in_progress || m_prepare_in_progress;
7089 const ImGuiStyle &style = ImGui::GetStyle();
7090 const float panel_w = m_imgui->scaled(21.5f);
7091 const float frame_h = ImGui::GetFrameHeight();
7092 const float icon_md = std::round(frame_h * 1.25f); // tool and view buttons
7093 const float icon_sm = std::round(frame_h * 1.05f); // buttons sitting in a row of text
7094 const float gap_s = std::round(m_imgui->scaled(0.4f));
7095 const float label_w = m_imgui->scaled(4.8f); // label column of a label + slider row
7096 const float card_pad = std::round(m_imgui->scaled(0.55f));
7097 const ImVec4 orca = ImGuiWrapper::COL_ORCA;
7098 const ImVec4 col_link = ImGuiWrapper::COL_ORCA;
7099 const ImVec4 col_frame = ImGui::GetStyleColorVec4(ImGuiCol_Separator);
7100 const ImU32 col_card = ImGui::GetColorU32(ImGuiCol_Text, dark ? 0.045f : 0.047f);
7101 const ImU32 col_line = ImGui::GetColorU32(ImGuiCol_Text, dark ? 0.08f : 0.09f);
7102 const ImU32 col_sep = ImGui::GetColorU32(ImGuiCol_Separator);
7103
7104 const auto scoped_combo = [&](const char *id, int *v, const char *const items[], int n) {
7106 const bool changed = ImGui::Combo(id, v, items, n);
7108 return changed;
7109 };
7110 const auto hover_tip = [&](const auto &text, const std::vector<TipMedia> &media = {}) {
7111 if (ImGui::IsItemHovered())
7112 panel_tooltip(text, media);
7113 };
7114
7115 // Framed icon toggle: a 1 px frame, and when on a teal frame over a teal tint with the icon in its
7116 // original colours (IconManager's color_wite_gray variant [1]); off, the theme's grey variant [0].
7117 // `unavailable`, when not empty, draws it faded but still hoverable, so the tooltip can say why it
7118 // cannot be used - a disabled ImGui item would swallow the hover and leave the user guessing.
7119 const auto icon_toggle = [&](int uid, const std::string &iconfile, bool active, float sz, const wxString &label,
7120 const wxString &tip_text, const std::vector<TipMedia> &media = {},
7121 const wxString &unavailable = wxString()) -> bool {
7122 const bool na = !unavailable.empty();
7123 const auto it = m_panel_icon_map.find(iconfile);
7124 const float pad = std::max(1.f, std::round(sz * 0.14f));
7125 const int variant = active ? 1 : 0;
7126 bool clicked = false;
7127 ImGui::PushID(uid);
7128 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, na ? style.Alpha * 0.32f : style.Alpha);
7129 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f);
7130 ImGui::PushStyleColor(ImGuiCol_Button, active ? ImVec4(orca.x, orca.y, orca.z, 0.22f) : ImVec4(0.f, 0.f, 0.f, 0.f));
7131 if (na) {
7132 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.f, 0.f, 0.f, 0.f));
7133 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.f, 0.f, 0.f, 0.f));
7134 }
7135 if (it != m_panel_icon_map.end() && int(it->second.size()) > variant && it->second[size_t(variant)]->is_valid()) {
7136 const IconManager::Icon &ic = *it->second[size_t(variant)];
7137 clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic.tex_id, ImVec2(sz - 2.f * pad, sz - 2.f * pad), ic.tl, ic.br,
7138 int(pad));
7139 } else {
7140 clicked = ImGui::Button(label.ToUTF8().data(), ImVec2(0.f, sz)); // the control is never lost to a missing icon
7141 }
7142 ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(),
7143 ImGui::GetColorU32(active ? orca : col_frame), style.FrameRounding);
7144 ImGui::PopStyleColor(na ? 3 : 1);
7145 ImGui::PopStyleVar(2);
7146 ImGui::PopID();
7147 if (ImGui::IsItemHovered())
7148 panel_tooltip(na ? tip_text + "\n\n" + unavailable : tip_text, media);
7149 return clicked && !na;
7150 };
7151 // Borderless icon button (non-toggle): always the grey monochrome variant.
7152 const auto icon_button = [&](int uid, const std::string &iconfile, float sz, const wxString &label,
7153 const wxString &tip_text, const std::vector<TipMedia> &media = {}) -> bool {
7154 const auto it = m_panel_icon_map.find(iconfile);
7155 const float pad = std::max(1.f, std::round(sz * 0.14f));
7156 bool clicked = false;
7157 ImGui::PushID(uid);
7158 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f);
7159 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.f, 0.f, 0.f, 0.f));
7160 if (it != m_panel_icon_map.end() && !it->second.empty() && it->second[0]->is_valid()) {
7161 const IconManager::Icon &ic = *it->second[0];
7162 clicked = m_imgui->image_button((ImTextureID) (intptr_t) ic.tex_id, ImVec2(sz - 2.f * pad, sz - 2.f * pad), ic.tl, ic.br,
7163 int(pad));
7164 } else {
7165 clicked = ImGui::Button(label.ToUTF8().data(), ImVec2(0.f, sz));
7166 }
7167 ImGui::PopStyleColor();
7168 ImGui::PopStyleVar();
7169 ImGui::PopID();
7170 if (!tip_text.empty() && ImGui::IsItemHovered())
7171 panel_tooltip(tip_text, media);
7172 return clicked;
7173 };
7174 // A short vertical rule between groups of buttons on one row; call it right after an item.
7175 const auto vsep = [&](float h) {
7176 ImGui::SameLine(0.f, gap_s);
7177 const ImVec2 p = ImGui::GetCursorScreenPos();
7178 ImGui::GetWindowDrawList()->AddLine(ImVec2(p.x, p.y + h * 0.2f), ImVec2(p.x, p.y + h * 0.8f), col_sep);
7179 ImGui::Dummy(ImVec2(1.f, h));
7180 ImGui::SameLine(0.f, gap_s);
7181 };
7182 const auto heading = [&](const wxString &text) {
7183 m_imgui->push_bold_font();
7184 m_imgui->text(text);
7185 m_imgui->pop_bold_font();
7186 };
7187 // Label column + a slider filling the rest of the row, value printed inside the track.
7188 // `right_inset` keeps a layer card's inner padding.
7189 const auto slider_label = [&](const wxString &label) {
7190 const float x0 = ImGui::GetCursorPosX();
7191 ImGui::AlignTextToFramePadding();
7192 m_imgui->text(label);
7193 ImGui::SameLine();
7194 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w));
7195 };
7196
7197 // ---- Typed values on sliders ----
7198 // Every slider in the panel goes through typed_slider(), so all of them take a number the same
7199 // two ways: Ctrl+click on the track, or the edit icon at the right end for the same box without
7200 // the modifier. Both end up in ImGui's own "temp input" mode - the icon only asks ImGui to focus
7201 // the slider, which is what turns it into an input box (see SliderScalar) - so the parsing,
7202 // the clamping and the Enter/click-away behaviour are ImGui's, not a second implementation.
7203
7204 // Retires a click whose row is no longer being drawn - the panel changed mode in between - so that
7205 // it cannot fire later when that row comes back. Before any row is drawn, so a live one still wins.
7206 if (!m_slider_type_request.empty() && ImGui::GetFrameCount() > m_slider_type_request_frame + 1)
7207 m_slider_type_request.clear();
7208
7209 // Exactly as tall as the slider it sits beside, so it lines up with the track without the row
7210 // having to be re-centred around it; icon_button() pads the glyph itself down to about 0.7 of this.
7211 const float edit_icon_w = frame_h;
7212 // What the slider's own width has to give up for the icon beside it.
7213 const float edit_icon_room = edit_icon_w + gap_s;
7214
7215 // One end of a range, through the slider's own format, so the bounds in the tooltip read exactly
7216 // like the value in the track. A parenthesised note in the format - "(auto)" - is dropped: it
7217 // describes the current state, not the bound.
7218 const auto fmt_bound = [](const char *format, auto v) {
7219 char buf[64];
7220 std::snprintf(buf, sizeof(buf), format, v);
7221 std::string out(buf);
7222 const size_t paren = out.find('(');
7223 if (paren != std::string::npos)
7224 out.erase(paren);
7225 while (!out.empty() && out.back() == ' ')
7226 out.pop_back();
7227 return out;
7228 };
7229 const auto range_text = [](const std::string &lo, const std::string &hi) {
7230 return Slic3r::format(_u8L("Range: %1% to %2%"), lo, hi);
7231 };
7232 const auto edit_icon_tip = [&](const std::string &range) {
7233 return _u8L("Type a value instead of dragging. Ctrl+click on the slider does the same.") + "\n\n" + range;
7234 };
7235
7236 // Draws `slider` at `item_w` (SetNextItemWidth's convention: negative measures back from the right
7237 // edge, and the caller has already left edit_icon_room in it), then the edit icon. The range goes on
7238 // both tooltips; `tip` and its illustrations `media` on the slider's. Returns whatever the slider returned.
7239 const auto typed_row = [&](const char *id, float item_w, const wxString &tip, const std::vector<TipMedia> &media,
7240 const std::string &range, const std::function<bool()> &slider) -> bool {
7241 // Deferred from the click a frame ago: SetKeyboardFocusHere() is a request for the *next*
7242 // frame, so it has to be issued before the slider it names, not from the icon beside it.
7243 if (m_slider_type_request == id) {
7244 ImGui::SetKeyboardFocusHere();
7245 m_slider_type_request.clear();
7246 m_parent.set_as_dirty(); // the focus only lands on a frame that actually gets drawn
7247 }
7248 ImGui::SetNextItemWidth(item_w);
7249 const bool changed = slider();
7250 // AllowWhenDisabled: several of these rows are drawn inside disabled_begin(), and a greyed
7251 // slider is exactly when the user needs to be told what it is and why it is not moving.
7252 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
7253 wxString full = tip;
7254 if (!full.IsEmpty())
7255 full += "\n\n";
7256 panel_tooltip(full + from_u8(range), media);
7257 }
7258 // SameLine() puts the cursor back at the slider's own top (ItemSize records the Y the item was
7259 // drawn at), so an icon of the same height lands on the track wherever the row placed it.
7260 ImGui::SameLine(0.f, gap_s);
7261 if (icon_button(int(ImGui::GetID(id)), "edit.svg", edit_icon_w, _L("Edit"), from_u8(edit_icon_tip(range)))) {
7263 m_slider_type_request_frame = ImGui::GetFrameCount();
7264 m_parent.set_as_dirty(); // the next frame is where the request above is turned into focus
7265 }
7266 return changed;
7267 };
7268
7269 // The two forms every slider in the panel is one of. They derive the range from the bounds they are
7270 // already given, so no row states its bounds or its format twice and the tooltip cannot drift from
7271 // the track. `item_w` is as typed_row() takes it.
7272 const auto typed_slider = [&](const char *id, float item_w, float *v, float v_min, float v_max, const char *format,
7273 bool log, const wxString &tip, const std::vector<TipMedia> &media = {}) -> bool {
7274 return typed_row(id, item_w, tip, media, range_text(fmt_bound(format, v_min), fmt_bound(format, v_max)), [&] {
7275 return ImGui::SliderFloat(id, v, v_min, v_max, format,
7276 ImGuiSliderFlags_AlwaysClamp | (log ? ImGuiSliderFlags_Logarithmic : 0));
7277 });
7278 };
7279 const auto typed_slider_int = [&](const char *id, float item_w, int *v, int v_min, int v_max, const char *format,
7280 bool log, const wxString &tip, const std::vector<TipMedia> &media = {}) -> bool {
7281 return typed_row(id, item_w, tip, media, range_text(fmt_bound(format, v_min), fmt_bound(format, v_max)), [&] {
7282 return ImGui::SliderInt(id, v, v_min, v_max, format,
7283 ImGuiSliderFlags_AlwaysClamp | (log ? ImGuiSliderFlags_Logarithmic : 0));
7284 });
7285 };
7286
7287 // Label column + the slider filling the rest of the row.
7288 const auto float_row = [&](const char *id, const wxString &label, float *v, float v_min, float v_max, const char *format,
7289 bool log, float right_inset, const wxString &tip, const std::vector<TipMedia> &media = {}) -> bool {
7290 slider_label(label);
7291 return typed_slider(id, -(std::max(right_inset, 1.f) + edit_icon_room), v, v_min, v_max, format, log, tip, media);
7292 };
7293 const auto int_row = [&](const char *id, const wxString &label, int *v, int v_min, int v_max, const char *format,
7294 float right_inset, const wxString &tip, const std::vector<TipMedia> &media = {}) -> bool {
7295 slider_label(label);
7296 return typed_slider_int(id, -(std::max(right_inset, 1.f) + edit_icon_room), v, v_min, v_max, format, false, tip, media);
7297 };
7298 const auto layer_name = [](const TextureDisplacementLayer &l) {
7299 return l.name.empty() ? Slic3r::format(_u8L("Layer %1%"), l.slot + 1) : l.name;
7300 };
7301 // Painted on any model part. The selectors flush into the model at the end of every stroke.
7302 const auto slot_painted = [mo](int slot) {
7303 for (const ModelVolume *v : mo->volumes)
7304 if (v->is_model_part() && !v->texture_displacement_facet(slot).empty())
7305 return true;
7306 return false;
7307 };
7308
7309 // ---- Header: title, Standard / Pro, dock toggle. Pinned above the body. ----
7310 {
7311 const float x0 = ImGui::GetCursorPosX();
7312 ImGui::AlignTextToFramePadding();
7313 heading(_L("Texture displacement"));
7314
7315 const std::string labels[2] = { _u8L_CONTEXT("Standard", "Mode"), _u8L("Pro") };
7316 const float seg_pad = m_imgui->scaled(0.5f);
7317 const float seg_w[2] = { ImGui::CalcTextSize(labels[0].c_str()).x + 2.f * seg_pad, ImGui::CalcTextSize(labels[1].c_str()).x + 2.f * seg_pad };
7318 ImGui::SameLine();
7319 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + panel_w - (seg_w[0] + seg_w[1] + gap_s + icon_sm)));
7320
7321 // Standard / Pro is a mode, not an option: Standard hides every mesh-preparation control and folds
7322 // the whole recipe into Bake, Pro shows all of it and hands the ordering to the user.
7323 const ImVec2 seg_min = ImGui::GetCursorScreenPos();
7324 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.f, style.ItemSpacing.y));
7325 ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.f);
7326 m_imgui->disabled_begin(busy);
7327 for (int m = 0; m < 2; ++m) {
7328 if (m > 0)
7329 ImGui::SameLine();
7330 const bool on = m_panel_mode == m;
7331 ImGui::PushStyleColor(ImGuiCol_Button, on ? orca : ImVec4(0.f, 0.f, 0.f, 0.f));
7332 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, on ? orca : ImVec4(orca.x, orca.y, orca.z, 0.25f));
7333 ImGui::PushStyleColor(ImGuiCol_ButtonActive, orca);
7334 ImGui::PushStyleColor(ImGuiCol_Text, on ? ImVec4(1.f, 1.f, 1.f, 1.f) : style.Colors[ImGuiCol_TextDisabled]);
7335 const bool clicked = ImGui::Button((labels[m] + "##panel_mode").c_str(), ImVec2(seg_w[m], frame_h));
7336 ImGui::PopStyleColor(4);
7337 hover_tip(m == 0 ? _u8L("Standard - pick a texture, paint where it goes, press Bake. Everything the "
7338 "mesh needs is done for you in that one step.") :
7339 _u8L("Pro - the mesh preparation is yours to run: Remesh, Subdivide and Bake as "
7340 "separate steps, with every setting on show. For when Standard's result is "
7341 "not what you wanted and you know why."));
7342 if (clicked && !on) {
7343 m_panel_mode = m;
7345 if (!pro_mode()) {
7346 // Leaving the subdivision preview open would strand a wireframe whose controls just
7347 // disappeared, so close it as part of the switch.
7348 m_subdivide_editing = false;
7353 }
7354 m_parent.set_as_dirty();
7355 }
7356 }
7357 m_imgui->disabled_end();
7358 ImGui::PopStyleVar(2);
7359 ImGui::GetWindowDrawList()->AddRect(seg_min, ImVec2(ImGui::GetItemRectMax().x, seg_min.y + frame_h),
7360 ImGui::GetColorU32(col_frame), style.FrameRounding);
7361
7362 ImGui::SameLine(0.f, gap_s);
7363 if (icon_button(806, "canvas_drag.svg", icon_sm, m_undocked ? _L("Dock panel") : _L("Undock panel"),
7364 _L("Detach this panel so it can be dragged anywhere over the 3D view, or dock it back beside "
7365 "the toolbar.")))
7367 wxGetApp().app_config->set_bool(UNDOCKED_CONFIG_KEY, m_undocked);
7368 }
7369 ImGui::Separator();
7370
7371 // ---- Body: everything between the header and the footer, scrolling as one. ----
7372 // Sized from last frame's content and footer heights (see m_panel_body_h): as tall as its content,
7373 // but never so tall that the footer drops below the bottom of the canvas.
7374 const float bottom = m_undocked ? ImGui::GetIO().DisplaySize.y : bottom_limit;
7375 const float max_body_h = std::max(m_imgui->scaled(8.f), bottom - ImGui::GetCursorScreenPos().y - m_panel_footer_h -
7376 style.WindowPadding.y - style.ItemSpacing.y);
7377 const float body_h = m_panel_body_h > 0.f ? std::min(m_panel_body_h + 1.f, max_body_h) : max_body_h;
7378
7379 push_slim_scrollbar_style(m_imgui->scaled(0.5f), dark);
7380 // NoScrollWithMouse: ease_wheel_scroll() eases the scroll instead of teleporting five text lines per
7381 // notch, which on blocks this tall lost the reader's place.
7382 ImGui::BeginChild("##td_body", ImVec2(panel_w, body_h), false, ImGuiWindowFlags_NoScrollWithMouse);
7384
7385 // Everything that changes the layer list is deferred until the list is no longer being drawn:
7386 // removing or reordering shifts mv->texture_displacement_layers under the pointers the loop holds,
7387 // and switching the active layer mid-loop would draw two cards open for a frame.
7388 int slot_to_remove = -1;
7389 int move_slot = -1;
7390 int move_to = -1;
7391 int activate_slot = -1;
7392
7393 TextureDisplacementLayer *active = mv != nullptr ? active_layer() : nullptr;
7394
7395 // ---- Paint: which layer strokes land in, and Paint / Erase ----
7396 {
7397 ImGui::AlignTextToFramePadding();
7398 heading(_L("Paint"));
7399 const std::string into = active != nullptr ? Slic3r::format(_u8L("into %1%"), layer_name(*active)) :
7400 _u8L("add a layer to paint");
7401 const float toggles_x = ImGui::GetWindowContentRegionMax().x - (2.f * icon_sm + gap_s);
7402 const float text_end = toggles_x - (2.f * gap_s + 1.f);
7403 ImGui::SameLine();
7404 const std::string shown = ellipsize(into, std::max(0.f, text_end - ImGui::GetCursorPosX()));
7405 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), text_end - ImGui::CalcTextSize(shown.c_str()).x));
7406 ImGui::TextDisabled("%s", shown.c_str());
7407 vsep(icon_sm);
7408 ImGui::SetCursorPosX(toggles_x);
7409 if (icon_toggle(820, "texture_displacement_add.svg", !m_erase_mode, icon_sm, _L("Paint"),
7410 _L("Paint - add the active layer where you click or drag. The right button erases, and so does "
7411 "holding Shift."),
7412 { { "paint.gif" } }))
7413 m_erase_mode = false;
7414 ImGui::SameLine(0.f, gap_s);
7415 if (icon_toggle(821, "texture_displacement_add_negative.svg", m_erase_mode, icon_sm, _L("Erase"),
7416 _L("Erase - remove the active layer where you click or drag. The right button paints."),
7417 { { "erase.gif" } }))
7418 m_erase_mode = true;
7419 }
7420
7421 // ---- Tools: brush / face / connected area, then the whole-model actions, then the active tool's own
7422 // control filling the rest of the row ----
7423 // "Face" and "Connected area" reuse the exact same selection machinery every other paint gizmo has
7424 // (single-facet click, and angle-limited flood fill respectively).
7425 {
7428 const bool is_area_mode = m_tool_type == ToolType::SMART_FILL;
7429 const float row_y = ImGui::GetCursorPosY();
7430 if (icon_toggle(801, "texture_displacement_brush.svg", is_brush_mode, icon_md, _L("Brush"),
7431 _L("Brush - paint over the surface by dragging"), { { "paint.gif" } })) {
7435 }
7436 ImGui::SameLine(0.f, gap_s);
7437 if (icon_toggle(802, "texture_displacement_face.svg", is_face_mode, icon_md, _L("Face"), _L("Face - click individual triangles"),
7438 { { "tool_face.gif" } })) {
7441 }
7442 ImGui::SameLine(0.f, gap_s);
7443 if (icon_toggle(803, "texture_displacement_connected_area.svg", is_area_mode, icon_md, _L("Connected area"),
7444 _L("Connected area - flood-fill the region reachable without crossing an edge sharper than the "
7445 "angle threshold"),
7446 { { "tool_smart_fill.gif" } })) {
7449 }
7450
7451 // Whole model: paint every face with the active layer, or clear its paint from all of them.
7452 const wxString whole_na = busy ? _L("Wait for the bake to finish.") :
7453 active == nullptr ? _L("Add a layer first.") :
7454 wxString();
7455 const wxString erase_na = !whole_na.empty() ? whole_na :
7456 !slot_painted(m_active_layer_slot) ? _L("The active layer has no paint yet.") :
7457 wxString();
7458 ImGui::SameLine(0.f, gap_s);
7459 if (icon_toggle(806, "texture_displacement_select_all.svg", false, icon_md, _L("Select whole model"),
7460 _L("Select whole model - paint every face of the model with the active layer"),
7461 { { "select_all.png" } }, whole_na))
7463 ImGui::SameLine(0.f, gap_s);
7464 if (icon_toggle(807, "texture_displacement_erase_all.svg", false, icon_md, _L("Erase whole model"),
7465 _L("Erase whole model - clear the active layer's paint from every face"),
7466 { { "erase_all.png" } }, erase_na)) {
7467 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Reset texture displacement selection"),
7469 int idx = -1;
7470 for (ModelVolume *v : mo->volumes)
7471 if (v->is_model_part()) {
7472 ++idx;
7473 m_triangle_selectors[idx]->reset();
7474 m_triangle_selectors[idx]->request_update_render_data(true);
7475 }
7477 m_parent.set_as_dirty();
7478 }
7479
7480 // The active tool's control fills the rest of the row, each part centred on the (taller) tool icons.
7481 const float row_end = ImGui::GetWindowContentRegionMax().x;
7482 const auto centre_on_row = [&](float h) { ImGui::SetCursorPosY(row_y + std::round((icon_md - h) * 0.5f)); };
7483 vsep(icon_md);
7484 if (is_brush_mode) {
7485 centre_on_row(frame_h);
7486 typed_slider("##cursor_radius",
7487 std::max(1.f, row_end - ImGui::GetCursorPosX() - (3.f * gap_s + 1.f + 2.f * icon_sm) - edit_icon_room),
7488 &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f mm", false, m_desc.at("cursor_size"),
7489 { { "cursor_size.gif" } });
7490 vsep(icon_sm);
7491 const bool is_circle = m_cursor_type == TriangleSelector::CursorType::CIRCLE;
7492 centre_on_row(icon_sm);
7493 if (icon_toggle(804, "circle_paint.svg", is_circle, icon_sm, m_desc.at("circle"),
7494 _L("Circle - paints everything under the brush as seen from the camera")))
7496 ImGui::SameLine(0.f, gap_s);
7497 centre_on_row(icon_sm);
7498 if (icon_toggle(805, "menu_obj_sphere.svg", !is_circle, icon_sm, m_desc.at("sphere"),
7499 _L("Sphere - paints only within a ball around the point under the cursor")))
7501 } else if (is_area_mode) {
7502 centre_on_row(frame_h);
7503 typed_slider("##smart_fill_angle", std::max(1.f, row_end - ImGui::GetCursorPosX() - edit_icon_room),
7505 _L("Angle threshold - the fill stops at edges sharper than this"),
7506 { { "smart_fill_angle_low.png", _L("Low") }, { "smart_fill_angle_high.png", _L("High") } });
7507 } else {
7508 centre_on_row(frame_h);
7509 ImGui::AlignTextToFramePadding();
7510 ImGui::TextDisabled("%s", ellipsize(_u8L("Click a triangle to paint it"),
7511 std::max(0.f, row_end - ImGui::GetCursorPosX())).c_str());
7512 }
7513 }
7514
7515 // ---- View: Normal / Fast / Checker / Distortion as one group, Wireframe on its own ----
7516 // The underlying state stays m_use_shaded_preview + m_uv_check_mode.
7517 {
7518 const int cur_mode = view_mode();
7519 int new_mode = cur_mode;
7520 int picked = -1; // the button clicked, the active one included
7521 bool wf_toggle = false;
7522 bool open_uv_editor = false;
7523 // Checker and Distortion both draw *the unwrap* - the first the texture grid laid over it, the second
7524 // its stretch - so they only mean anything for a layer mapped with Unwrap (LSCM). On the default
7525 // triplanar mapping (or cylindrical / spherical / from view) they are faded out with the reason in the
7526 // tooltip, rather than being offered and then showing nothing.
7527 const wxString uv_view_na = active == nullptr ? _L("Add a layer first.") :
7528 active->projection_method != TextureProjectionMethod::LSCM ?
7529 _L("Only for a layer mapped with Unwrap (LSCM) - set the "
7530 "active layer's Mapping to Unwrap to use this view.") :
7531 wxString();
7532 // Either view over a layer that stopped being an unwrap shows nothing at all, so fall back to Normal.
7533 if ((cur_mode == 2 || cur_mode == 3) && !uv_view_na.empty())
7534 new_mode = 0;
7535
7536 const float x0 = ImGui::GetCursorPosX();
7537 ImGui::AlignTextToFramePadding();
7538 ImGui::TextDisabled("%s", _u8L("View").c_str());
7539 ImGui::SameLine();
7540 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + m_imgui->scaled(2.6f)));
7541 if (icon_toggle(701, "texture_displacement_real_preview.svg", cur_mode == 0, icon_md, _L("Normal"),
7542 _L("Normal - the real geometry, exactly what Bake will produce. Slower to update"),
7543 { { "tile_on.png" } }))
7544 new_mode = picked = 0;
7545 ImGui::SameLine(0.f, gap_s);
7546 if (icon_toggle(702, "texture_displacement_fast_preview.svg", cur_mode == 1, icon_md, _L("Fast"),
7547 _L("Fast - the relief is only shaded on, not built, and only for the layer you are "
7548 "editing. Updates instantly while you paint"),
7549 { { "view_fast.png" } }))
7550 new_mode = picked = 1;
7551 ImGui::SameLine(0.f, gap_s);
7552 if (icon_toggle(703, "texture_displacement_checker.svg", cur_mode == 2, icon_md, _L("Checker"),
7553 _L("Checker - a test grid instead of the texture. Where the squares stay square the "
7554 "texture is undistorted; where they stretch, it will too. Opens the UV editor if "
7555 "it is closed"),
7556 { { "view_checker.png" } }, uv_view_na)) {
7557 new_mode = picked = 2;
7558 open_uv_editor = true;
7559 }
7560 ImGui::SameLine(0.f, gap_s);
7561 if (icon_toggle(704, "texture_displacement_distortion.svg", cur_mode == 3, icon_md, _L("Distortion"),
7562 _L("Distortion - blue-to-red stretch heatmap over the unwrap. Opens the UV editor if "
7563 "it is closed"),
7564 { { "view_distortion.png" } }, uv_view_na)) {
7565 new_mode = picked = 3;
7566 open_uv_editor = true;
7567 }
7568 vsep(icon_md);
7569 if (icon_toggle(705, "texture_displacement_wireframe.svg", m_wireframe_overlay, icon_md, _L("Wireframe"),
7570 _L("Wireframe - overlay the mesh edges; independent of the view above"),
7571 { { "view_wireframe.png" } }))
7572 wf_toggle = true;
7573
7574 const std::string auto_label = _u8L("Auto");
7575 const float auto_w = frame_h * 0.78f /*ratio from BBLCheckbox*/ + style.ItemInnerSpacing.x + ImGui::CalcTextSize(auto_label.c_str()).x;
7576 ImGui::SameLine();
7577 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - auto_w));
7578 if (m_imgui->bbl_checkbox(wxString::FromUTF8(auto_label) + "##auto_update", m_auto_update) && m_auto_update)
7579 rebuild_preview(); // catch up anything that changed while it was off
7580 hover_tip(_u8L("Rebuilds the preview as soon as anything changes. Turn it off on a heavy model if painting "
7581 "or dragging a slider starts to stutter - the preview then waits until you let go."));
7582
7583 // Both are views of the unwrap, so picking one brings the UV editor up with it - including when that
7584 // view is already the active one and only the pane is missing.
7585 if (open_uv_editor && !m_show_uv_editor) {
7586 m_show_uv_editor = true;
7587 if (new_mode == cur_mode)
7588 update_uv_editor(); // otherwise apply_view_mode() below does it
7589 }
7590 if (picked >= 0)
7591 m_island_view.picked(picked);
7592 if (new_mode != cur_mode)
7593 apply_view_mode(new_mode);
7594 if (wf_toggle) {
7597 m_parent.set_as_dirty();
7598 }
7599 }
7600
7601 ImGui::Separator();
7602 // ---- Texture layers: the active layer as an open card, every other one as a single row ----
7603 {
7604 ImGui::AlignTextToFramePadding();
7605 heading(_L("Texture layers"));
7606 if (mv != nullptr) {
7607 const std::string count = std::to_string(mv->texture_displacement_layers.size()) + " / " +
7608 std::to_string(TEXTURE_DISPLACEMENT_MAX_LAYERS);
7609
7610 const std::string add_label = "+ " + _u8L("Add layer");
7611
7612 ImGui::SameLine();
7613 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), ImGui::GetWindowContentRegionMax().x - ImGui::CalcTextSize(count.c_str()).x - ImGui::CalcTextSize(add_label.c_str()).x) - (style.FrameBorderSize + style.FramePadding.x) * 2.f - style.ItemSpacing.x);
7614 ImGui::TextDisabled("%s", count.c_str());
7615
7616 ImGui::SameLine();
7617 const bool full = mv->texture_displacement_layers.size() >= TEXTURE_DISPLACEMENT_MAX_LAYERS;
7618 //Slic3r::format(into_u8(_L_PLURAL("All %1% layer used", "All %1% layers used", unsigned(TEXTURE_DISPLACEMENT_MAX_LAYERS))), TEXTURE_DISPLACEMENT_MAX_LAYERS)
7619 m_imgui->disabled_begin(busy || full);
7620 if (ImGui::Button((add_label + "##add_layer").c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0.f)))
7622 m_imgui->disabled_end();
7623 }
7624 }
7625
7626 std::vector<TextureDisplacementLayer *> ordered;
7627 if (mv != nullptr) {
7628 for (TextureDisplacementLayer &l : mv->texture_displacement_layers)
7629 ordered.push_back(&l);
7630 std::sort(ordered.begin(), ordered.end(), [](const auto *a, const auto *b) { return a->slot < b->slot; });
7631 }
7632
7633 ImDrawList *dl = ImGui::GetWindowDrawList();
7634 const float content_rx = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x;
7635 const float rounding = style.FrameRounding + 1.f;
7636
7637 // Opens the texture library for a layer, making it the active one too (after the loop).
7638 const auto open_picker = [&](int slot) {
7639 m_picker_slot = slot;
7640 m_picker_open_request = true;
7642 };
7643 // The six-dot drag icon, drawn into `list` over [x, x + w] x [y, y + h].
7644 const auto draw_drag_icon = [&](ImDrawList *list, float x, float y, float w, float h, ImU32 tint) {
7645 const auto it = m_panel_icon_map.find("texture_displacement_drag.svg");
7646 if (it != m_panel_icon_map.end() && !it->second.empty() && it->second[0]->is_valid()) {
7647 // The icon's dots sit in the middle of its square, so it is drawn at the row's height centred on the
7648 // narrow hit area; only its transparent margins overhang the neighbouring widgets.
7649 const IconManager::Icon &ic = *it->second[0];
7650 const float cx = x + 0.5f * w;
7651 list->AddImage((ImTextureID) (intptr_t) ic.tex_id, ImVec2(cx - 0.5f * h, y), ImVec2(cx + 0.5f * h, y + h), ic.tl, ic.br, tint);
7652 } else {
7653 const float r = std::max(1.f, std::round(m_imgui->scaled(0.08f)));
7654 for (int row = 0; row < 3; ++row)
7655 for (int col = 0; col < 2; ++col)
7656 list->AddCircleFilled(ImVec2(x + w * (0.3f + 0.4f * col), y + h * 0.5f + float(row - 1) * 4.f * r), r, tint);
7657 }
7658 };
7659 const auto drag_tint = [dark](int alpha) { return dark ? IM_COL32(255, 255, 255, alpha) : IM_COL32(90, 90, 90, alpha); };
7660
7661 const float grip_w = std::round(m_imgui->scaled(0.7f));
7662 // Six-dot handle that drags a layer onto another one to reorder the stack.
7663 const auto grip = [&](const TextureDisplacementLayer &l, float h) {
7664 const ImVec2 p = ImGui::GetCursorScreenPos();
7665 ImGui::InvisibleButton("##grip", ImVec2(grip_w, h));
7666 const bool hot = ImGui::IsItemHovered() || ImGui::IsItemActive();
7667 draw_drag_icon(dl, p.x, p.y, grip_w, h, drag_tint(hot ? 255 : 150));
7668 hover_tip(_u8L("Drag onto another layer to reorder. The first layer sits on the bare surface; each one after "
7669 "it adds its relief on top of the ones before."));
7670 // ImGui's own preview is a tooltip holding whatever is submitted here. The panel draws a copy of the whole
7671 // layer row under the cursor instead (after the layer list), so that one is switched off.
7672 if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip)) {
7673 const int slot = l.slot;
7674 ImGui::SetDragDropPayload("TD_LAYER", &slot, sizeof(slot));
7675 ImGui::EndDragDropSource();
7676 }
7677 };
7678
7679 // The layer being dragged, while a reorder drag is in flight. Its card stays in place as a faded ghost.
7680 int dragged_slot = -1;
7681 if (const ImGuiPayload *payload = ImGui::GetDragDropPayload(); payload != nullptr && payload->IsDataType("TD_LAYER"))
7682 dragged_slot = *static_cast<const int *>(payload->Data);
7683 int dragged_index = -1;
7684 for (size_t i = 0; i < ordered.size(); ++i)
7685 if (ordered[i]->slot == dragged_slot)
7686 dragged_index = int(i);
7687 const ImU32 ghost_col = ImGui::GetColorU32(ImGuiCol_WindowBg, 0.65f);
7688
7689 // A layer card or row [mn, mx] as a drop target. Dropped anywhere on another layer, the dragged one takes its
7690 // place and the layers in between shift over by one - so it lands above the target when moving up and below it
7691 // when moving down. A teal line on that side shows where; ImGui's default highlight rectangle says only
7692 // "something is here", so it is replaced by the line.
7693 //
7694 // Deciding by which half of the target the cursor is over instead made dropping onto a neighbour's near half a
7695 // silent no-op ("after the layer I am already after"), and that is exactly where a drag to the top ends.
7696 const auto drop_target = [&](size_t index, int slot, const ImVec2 &mn, const ImVec2 &mx) {
7697 if (!ImGui::BeginDragDropTarget())
7698 return;
7699 if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("TD_LAYER", ImGuiDragDropFlags_AcceptBeforeDelivery |
7700 ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) {
7701 const int from = *static_cast<const int *>(payload->Data);
7702 const bool after = dragged_index >= 0 && dragged_index < int(index);
7703 if (from != slot) {
7704 const float y = after ? mx.y + 2.f : mn.y - 2.f;
7705 dl->AddLine(ImVec2(mn.x, y), ImVec2(mx.x, y), ImGui::GetColorU32(orca), 2.f);
7706 }
7707 if (payload->IsDelivery()) {
7708 move_slot = from;
7709 move_to = int(index) + (after ? 1 : 0);
7710 }
7711 }
7712 ImGui::EndDragDropTarget();
7713 };
7714
7715 for (size_t li = 0; li < ordered.size(); ++li) {
7716 TextureDisplacementLayer &layer = *ordered[li];
7717 const bool is_active = layer.slot == m_active_layer_slot;
7718 const bool painted = slot_painted(layer.slot);
7719 ImGui::PushID(layer.slot);
7720
7721 if (is_active) {
7722 // The card's height is not known until it is laid out, so its content goes into a foreground
7723 // channel and the backing rectangle into a background one, merged at the end - the standard
7724 // ImGui "rect behind a group" trick.
7725 const ImVec2 card_min = ImGui::GetCursorScreenPos();
7726 dl->ChannelsSplit(2);
7727 dl->ChannelsSetCurrent(1);
7728 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + card_pad);
7729 ImGui::Indent(card_pad);
7730 ImGui::BeginGroup();
7731
7732 // Header: grip, thumbnail and name (both open the library), paint state, order, remove.
7733 {
7734 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(gap_s, style.ItemSpacing.y));
7735 grip(layer, frame_h);
7736 ImGui::SameLine();
7737 if (GLTexture *thumb = get_layer_thumbnail(layer)) {
7738 if (ImGui::ImageButton((ImTextureID) (intptr_t) thumb->get_id(), ImVec2(frame_h, frame_h), ImVec2(0.f, 0.f),
7739 ImVec2(1.f, 1.f), 0))
7740 open_picker(layer.slot);
7741 } else if (ImGui::Button("?##thumb", ImVec2(frame_h, frame_h))) {
7742 open_picker(layer.slot);
7743 }
7744 hover_tip(_u8L("Pick a different image for this layer. The paint, depth and tiling stay as they are."));
7745 {
7746 // The teal outline on the thumbnail is what marks this card as the layer being painted.
7747 const ImVec2 a = ImGui::GetItemRectMin(), b = ImGui::GetItemRectMax();
7748 dl->AddRect(ImVec2(a.x - 2.f, a.y - 2.f), ImVec2(b.x + 2.f, b.y + 2.f), ImGui::GetColorU32(orca), style.FrameRounding);
7749 }
7750 ImGui::SameLine();
7751
7752 const std::string not_painted = _u8L("not painted");
7753 const float right_w = (painted ? 0.f : ImGui::CalcTextSize(not_painted.c_str()).x + gap_s) + 4.f * frame_h + 3.f * gap_s;
7754 const float name_w = std::max(frame_h, ImGui::GetWindowContentRegionMax().x - card_pad - ImGui::GetCursorPosX() - gap_s - right_w);
7755 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(orca.x, orca.y, orca.z, 0.2f));
7756 ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(orca.x, orca.y, orca.z, 0.35f));
7757 ImGui::AlignTextToFramePadding();
7758 const std::string shown = ellipsize(layer_name(layer), name_w - 2.f * style.FramePadding.x);
7759 if (ImGui::Selectable((shown + "##name").c_str(), false, 0, ImVec2(name_w, frame_h)))
7760 open_picker(layer.slot);
7761 ImGui::PopStyleColor(2);
7762 hover_tip(_u8L("Pick a different image for this layer. The paint, depth and tiling stay as they are."));
7763 if (!painted) {
7764 ImGui::SameLine();
7765 ImGui::TextDisabled("%s", not_painted.c_str());
7766 }
7767 // Between the name and the order arrows: it acts on the image the name refers to, so it
7768 // belongs with the name rather than with the controls that reorder the stack.
7769 ImGui::SameLine();
7770 m_imgui->disabled_begin(busy || layer.empty());
7771 if (icon_button(613 + layer.slot, "cog.svg", frame_h, _L("Adjust image"),
7772 _u8L("Brightness, contrast, sharpness, blur and hue, shown on the image and on the "
7773 "model as you set them. Save as copy writes the result to your texture library "
7774 "and points this layer at it.")))
7775 open_image_adjust(layer);
7776 m_imgui->disabled_end();
7777 ImGui::SameLine();
7778 m_imgui->disabled_begin(busy || li == 0);
7779 if (icon_button(611, "texture_displacement_move_up.svg", frame_h, _L("Move up"), _L("Move up - applied earlier"))) {
7780 move_slot = layer.slot;
7781 move_to = int(li) - 1;
7782 }
7783 m_imgui->disabled_end();
7784 ImGui::SameLine();
7785 m_imgui->disabled_begin(busy || li + 1 >= ordered.size());
7786 if (icon_button(612, "texture_displacement_move_down.svg", frame_h, _L("Move down"), _L("Move down - applied later"))) {
7787 move_slot = layer.slot;
7788 move_to = int(li) + 2;
7789 }
7790 m_imgui->disabled_end();
7791 ImGui::SameLine();
7792 m_imgui->disabled_begin(busy);
7793 if (icon_button(600 + layer.slot, "texture_displacement_cross.svg", frame_h, m_desc.at("remove_layer"),
7794 _L("Remove this layer")))
7795 slot_to_remove = layer.slot;
7796 m_imgui->disabled_end();
7797 ImGui::PopStyleVar();
7798 }
7799
7800 // The three controls nearly every layer needs; everything else waits behind "More settings".
7801 // Depth and tile size are logarithmic, so both ends of their wide ranges stay usable.
7802 m_preview_params_dirty |= float_row("##depth", _L("Depth"), &layer.depth_mm, 0.01f, 10.f, "%.3f mm", true, card_pad,
7803 _L("Height of the relief: how far white in the texture lifts the surface, in "
7804 "millimetres. Black does not move it at all, unless Midlevel says otherwise."),
7805 { { "depth.gif" } });
7806 // Mapped by the model's own UV layout there is no millimetre scale to set: the layout is the
7807 // placement (see TextureProjectionMethod::ModelUV), so the slider would move nothing. The row
7808 // states why rather than hiding: float_row()'s tooltip is shown for a disabled slider too.
7809 const bool tile_size_na = layer.projection_method == TextureProjectionMethod::ModelUV;
7810 m_imgui->disabled_begin(tile_size_na);
7811 m_preview_params_dirty |= float_row("##tile_size", _L("Tile size"), &layer.tiling_scale, 0.2f, 200.f, "%.2f mm", true, card_pad,
7812 tile_size_na ?
7813 _L("Mapped by the model's own UV layout, which is already the placement - one "
7814 "copy of the texture covers it exactly, so there is no tile size to set.") :
7815 _L("How wide one copy of the texture is on the model. Smaller repeats the pattern "
7816 "more often and makes its detail finer; with Tile off, this is the size of the "
7817 "single copy."),
7818 tile_size_na ? std::vector<TipMedia>{} :
7819 std::vector<TipMedia>{ { "tile_size_large.png", _L("Larger") },
7820 { "tile_size_small.png", _L("Smaller") } });
7821 m_imgui->disabled_end();
7822 m_preview_params_dirty |= float_row("##rotation", _L("Rotation"), &layer.rotation_deg, 0.f, 360.f, "%.0f°", false, card_pad,
7823 _L("Turns the texture on the surface, in degrees - for lining a pattern up with an "
7824 "edge of the model."),
7825 { { "rotation.gif" } });
7826
7827 if (m_layer_expanded[size_t(layer.slot)]) {
7828 {
7829 // A dashed rule between the basic and the advanced settings.
7830 const ImVec2 p = ImGui::GetCursorScreenPos();
7831 const float x1 = content_rx - card_pad;
7832 const float dash = std::round(m_imgui->scaled(0.25f));
7833 for (float xx = p.x; xx < x1; xx += 2.f * dash)
7834 dl->AddLine(ImVec2(xx, p.y), ImVec2(std::min(xx + dash, x1), p.y), col_line);
7835 ImGui::Dummy(ImVec2(1.f, 1.f));
7836 }
7837
7838 // Midlevel: the height that means "don't move". At 0 the surface only ever bulges outwards;
7839 // at 0.5 mid-grey is neutral and darker texels cut inwards.
7840 m_preview_params_dirty |= float_row("##midlevel", _L("Midlevel"), &layer.midlevel, 0.f, 10.f, "%.2f", false, card_pad,
7841 _L("Which gray stays where the surface already is. At 0 the texture only pushes "
7842 "outwards; at 0.5 mid-gray stays put, so darker grays cut in and lighter ones "
7843 "still push out - one image both embosses and engraves.\n\n"
7844 "What cuts in has to fit: inside a sharp corner or through a thin wall, a deep "
7845 "cut can pass through the other side."),
7846 { { "midlevel.gif" } });
7847 if (layer.midlevel > 0.f && layer.depth_mm > 1.f) {
7848 ImGui::PushTextWrapPos(content_rx - card_pad);
7849 m_imgui->warning_text(_L("Deep inward displacement may self-intersect."));
7850 ImGui::PopTextWrapPos();
7851 }
7852
7853 m_preview_params_dirty |= float_row("##smoothing", _L("Smoothing"), &layer.smoothing, 0.f, 1.f, "%.2f", false, card_pad,
7854 _L("Blurs the image before it is used, which rounds off hard steps and removes "
7855 "speckle from a noisy photo. Raise it if the relief looks harsh or grainy; it "
7856 "costs fine detail."),
7857 { { "smoothing_off.png", _L("Off") }, { "smoothing_on.png", _L("On") } });
7858
7859 // Edge fade: the relief flattens toward the boundary of the painted area.
7860 m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Edge fade") + "##edge_smoothing", layer.edge_smoothing);
7861 hover_tip(_u8L("Flattens the relief as it approaches the edge of the painted area, so it "
7862 "blends into the bare surface instead of stopping at a step."),
7863 { { "edge_fade_off.png", _L("Off") }, { "edge_fade_on.png", _L("On") } });
7864 ImGui::SameLine();
7865 m_imgui->disabled_begin(!layer.edge_smoothing);
7866 m_preview_params_dirty |= typed_slider("##edge_amount", -(card_pad + edit_icon_room),
7867 &layer.edge_smoothing_amount, 0.02f, 1.f, "%.2f", false,
7868 _L("How far in the fade reaches, as a share of the painted area. Small "
7869 "values soften a narrow band at the edge; 1 flattens almost all of it."));
7870 m_imgui->disabled_end();
7871
7872 // Invert and Colours share a row.
7873 {
7874 const float x0 = ImGui::GetCursorPosX();
7875 m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Invert"), layer.invert);
7876 hover_tip(_u8L("Turns the relief inside out: what stood out is cut in, and the other way "
7877 "round. The same as using a negative of the image."),
7878 { { "invert_off.png", _L("Off") }, { "invert_on.png", _L("On") } });
7879 ImGui::SameLine();
7880 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + 0.5f * (ImGui::GetWindowContentRegionMax().x - card_pad - x0)));
7881
7882 // Only offered for a texture that actually has colour - the shipped library is grayscale,
7883 // and a checkbox that silently does nothing on nine textures out of ten is worse than no
7884 // checkbox. Disabled rather than hidden so it is clear the feature exists and what it wants.
7885 const bool has_color = decode_height_texture(layer).has_color();
7886 bool color_enabled = layer.color_enabled && has_color;
7887 m_imgui->disabled_begin(!has_color);
7888 if (m_imgui->bbl_checkbox(_L("Colors"), color_enabled)) {
7889 layer.color_enabled = color_enabled;
7891 }
7892 m_imgui->disabled_end();
7893 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
7894 panel_tooltip(has_color ?
7895 _u8L("Prints the painted area in the texture's colors as well as its "
7896 "relief. Each color is matched to the nearest of your loaded "
7897 "filaments; anything you did not paint keeps the object's own.") :
7898 _u8L("This texture is a grayscale height map, so it has no colors to "
7899 "apply. Import a color image to use this."));
7900
7901 // The rest of colour belongs to the whole stack, not to this layer, so it only appears once -
7902 // under whichever layer turned colour on.
7903 if (color_enabled) {
7904 TextureDisplacementOptions &opts = mv->texture_displacement_options;
7905 if (m_imgui->bbl_checkbox(_L("Mix filaments"), opts.color_mix_enabled))
7907 hover_tip(_u8L("Interleaves two filaments to fake the colors in between, so a handful "
7908 "of filaments can cover a photo or a gradient. An image of flat colors "
7909 "prints the same either way. Off uses one filament per area."),
7910 { { "mix_off.png", _L("Off") }, { "mix_on.png", _L("On") } });
7911 if (opts.color_mix_enabled) {
7912 cached_palette(); // brings m_palette_filaments up to date
7913 // Every mix can become a filament slot, and there are only so many beside the
7914 // physical filaments.
7915 const int max_mixes = std::max(1, int(EnforcerBlockerType::ExtruderMax) - int(m_palette_filaments.size()));
7916 if (int_row("##color_mix_count", _L("Mixed colors"), &opts.color_mix_count, 1, max_mixes, "%d", card_pad,
7917 _L("The most mixed filaments a bake may add. They are picked from the "
7918 "texture's colors, and only the ones the bake actually uses are created."),
7919 { { "mixed_colors_few.png", _L("Few") }, { "mixed_colors_many.png", _L("Many") } }))
7921 // After the slider, so a change shows in the same frame.
7922 const int n_colors = int(cached_palette().size());
7923 const int n_filaments = int(m_palette_filaments.size());
7924 // TRN The %2% part of "%1% printable colors %2%"; %1% is the number of filaments.
7925 const std::string from_filaments = Slic3r::format(into_u8(_L_PLURAL("from %1% filament", "from %1% filaments", n_filaments)), n_filaments);
7926 // TRN %1% is the number of printable colors, %2% is "from N filaments".
7927 ImGui::TextDisabled("%s", Slic3r::format(into_u8(_L_PLURAL("%1% printable color %2%", "%1% printable colors %2%", n_colors)),
7928 n_colors, from_filaments).c_str());
7929 }
7930 if (int_row("##color_despeckle", _L("Denoise"), &opts.color_despeckle, 0, 6, "%d", card_pad,
7931 _L("Cleans up single stray triangles of the wrong color, which detail finer "
7932 "than the mesh leaves behind. Raise it if the result looks speckled, "
7933 "lower it if small features are being swallowed."),
7934 { { "denoise_off.png", _L("Off") }, { "denoise_on.png", _L("On") } }))
7936 }
7937 }
7938
7939 // Mapping, as one icon per TextureProjectionMethod, in the enum's own order.
7940 {
7941 const float x0 = ImGui::GetCursorPosX();
7942 ImGui::AlignTextToFramePadding();
7943 ImGui::TextDisabled("%s", _u8L_CONTEXT("Mapping", "Texture Displacement").c_str());
7944 ImGui::SameLine();
7945 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w));
7946 struct MappingIcon
7947 {
7948 const char *file;
7949 wxString label;
7950 wxString tip;
7951 const char *picture; // the same ball with the checker laid on it this way, or null for none
7952 };
7953 const MappingIcon mappings[] = {
7954 { "menu_obj_cube.svg", _L("Triplanar (blended)"),
7955 _L("Triplanar - projects the texture from all three axes at once and blends between them, so a "
7956 "patch wrapping around a sharp edge has no seam."),
7957 "map_cube.png" },
7958 { "menu_obj_cylinder.svg", _L("Cylindrical"),
7959 _L("Cylindrical - wraps the texture around the painted area's own center, for round shapes."),
7960 "map_cylinder.png" },
7961 { "menu_obj_sphere.svg", _L("Spherical"),
7962 _L("Spherical - wraps the texture around the painted area's own center in both directions."),
7963 "map_sphere.png" },
7964 { "texture_displacement_map_unwrap.svg", _L("Unwrap (LSCM)"),
7965 _L("Unwrap - flattens the painted area and maps the texture onto it with as little stretching as "
7966 "possible. The area is cut into pieces at its sharp edges first (see Seam angle), so each "
7967 "piece can lie flat on its own."),
7968 "map_unwrap.png" },
7969 { "texture_displacement_map_view.svg", _L("From view"),
7970 _L("From view - projects straight onto the painted area from where you are looking, like a slide "
7971 "projector."),
7972 "map_view.png" },
7973 { "texture_displacement_map_model_uv.svg", _L("Model UV"),
7974 _L("Model UV - uses the texture coordinates the model itself came with, so a texture made for "
7975 "that model lands exactly where it was painted. Nothing is unwrapped, and Tile size does "
7976 "not apply: the model's own layout is the placement."),
7977 nullptr },
7978 };
7979 // Only a volume that actually carries a UV channel can be mapped by it. Shown
7980 // greyed out rather than hidden, so the reason is visible instead of the control
7981 // simply being missing.
7982 const wxString model_uv_na =
7983 (mv != nullptr && mv->has_texture_uv()) ?
7984 wxString() :
7985 _L("This model carries no texture coordinates of its own. Only a model imported with a UV "
7986 "channel - a textured OBJ, say - can be mapped by them.");
7987 for (int mi = 0; mi < int(IM_ARRAYSIZE(mappings)); ++mi) {
7988 if (mi > 0)
7989 ImGui::SameLine(0.f, gap_s);
7990 const auto method = static_cast<TextureProjectionMethod>(mi);
7991 const wxString na = (method == TextureProjectionMethod::ModelUV) ? model_uv_na : wxString();
7992 const std::vector<TipMedia> picture = mappings[mi].picture != nullptr ?
7993 std::vector<TipMedia>{ { mappings[mi].picture } } :
7994 std::vector<TipMedia>{};
7995 if (icon_toggle(830 + mi, mappings[mi].file, layer.projection_method == method, icon_sm, mappings[mi].label,
7996 mappings[mi].tip, picture, na) &&
7997 layer.projection_method != method) {
7998 // Capture the current view the moment "From view" is chosen, so it does something
7999 // sensible immediately rather than projecting from a stale/default direction.
8000 if (method == TextureProjectionMethod::ViewProjected)
8001 capture_view_projection(layer);
8002 layer.projection_method = method;
8003 m_preview_params_dirty = true;
8004 // Unwrap and its tools live in the UV editor pane, so picking it opens the pane
8005 // (via the preview rebuild this triggers).
8006 if (method == TextureProjectionMethod::LSCM)
8007 m_show_uv_editor = true;
8008 }
8009 }
8010 }
8011
8012 if (layer.projection_method == TextureProjectionMethod::LSCM) {
8013 // Unwrap and everything that edits it live in the UV editor pane, next to the islands they change.
8014 // Picking this mapping opens the pane; this brings it back after it has been closed.
8015 if (m_imgui->button(_u8L("Open UV editor"))) {
8016 m_show_uv_editor = true;
8018 }
8019 hover_tip(_u8L("Opens the UV editor, where you can see how the texture is laid out over the "
8020 "painted area, cut seams and move the pieces around."));
8021 ImGui::SameLine();
8022 ImGui::AlignTextToFramePadding();
8023 if (m_uv_editor_unwrap.empty())
8024 ImGui::TextDisabled("%s", _u8L("Not unwrapped yet").c_str());
8025 else
8026 ImGui::TextDisabled("%s", Slic3r::format(into_u8(_L_PLURAL("%1% island", "%1% islands", m_uv_editor_unwrap.chart_count)),
8027 m_uv_editor_unwrap.chart_count).c_str());
8028 }
8029
8030 if (layer.projection_method == TextureProjectionMethod::ViewProjected) {
8031 // Re-capture the projector from wherever the camera is now: orbit the model, press this, and
8032 // the texture is re-laid from the new angle.
8033 if (m_imgui->button(_u8L("Capture current view"))) {
8035 // Selecting the visible faces *after* capturing means the projector axes are already the
8036 // ones the selection was made against - the two describe the same viewpoint.
8038 show_error(nullptr, _u8L("Nothing is visible from this angle - turn the model to face the "
8039 "part you want to project onto."));
8042 }
8043 hover_tip(_u8L("Projects the texture onto the painted area from where you are looking now, like "
8044 "a slide projector. Faces turned away from you stretch, so line the view up with "
8045 "the surface you care about first."));
8046
8047 if (m_imgui->bbl_checkbox(_L("Project only on visible"), m_project_only_visible)) {
8049 show_error(nullptr, _u8L("Nothing is visible from this angle - turn the model to face the "
8050 "part you want to project onto."));
8053 }
8054 hover_tip(_u8L("Paints only the faces you can actually see right now - facing you and not hidden "
8055 "behind anything - and projects onto those. Replaces what the layer had painted."));
8056
8057 bool projector_open = m_projector_frame != nullptr && m_projector_frame->IsShown();
8058 if (m_imgui->bbl_checkbox(_L("Projection frame"), projector_open))
8059 show_projector(projector_open);
8060 hover_tip(_u8L("Opens a window you drag over the model. Whatever you can see through it is what "
8061 "gets the texture, and its border becomes the edge of the projection."));
8062
8063 if (projector_open) {
8064 if (int_row("##projector_opacity", _L("Opacity"), &m_projector_opacity, 20, 255, "%d", card_pad,
8065 _L("How solid the frame window looks while you place it. Lower to see the "
8066 "model through it; it does not affect the result.")))
8068
8069 if (m_imgui->button(_u8L("Apply projection frame"))) {
8070 const int painted_count = apply_projection_frame();
8071 if (painted_count == 0)
8072 show_error(nullptr, _u8L("Nothing of the model is inside the frame - move it over the "
8073 "part you want to project onto."));
8074 else if (painted_count < 0)
8075 show_error(nullptr, _u8L("The frame could not be applied. Make sure it overlaps the 3D view."));
8076 }
8077 hover_tip(_u8L("Projects the texture through the frame from where you are looking now and "
8078 "paints the faces inside it. Replaces what the layer had painted; the result "
8079 "sticks to the model, so you can orbit afterwards."));
8080 }
8081
8082 if (layer.view_project_projective) {
8083 ImGui::AlignTextToFramePadding();
8084 ImGui::TextDisabled("%s", _u8L("Placed by projection frame.").c_str());
8085 ImGui::SameLine();
8086 if (m_imgui->button(_u8L("Clear"))) {
8087 // Back to the plain axis projection, where tiling/rotation/offset mean something again -
8088 // the matrix path deliberately ignores them.
8089 layer.view_project_projective = false;
8090 layer.tile_enabled = true;
8092 }
8093 }
8094 }
8095
8096 // Tile, and how it repeats.
8097 {
8098 const float x0 = ImGui::GetCursorPosX();
8099 m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Tile") + "##tile_enabled", layer.tile_enabled);
8100 hover_tip(_u8L("Repeats the texture across the painted area. Off places one copy, like a decal, "
8101 "at the size set by Tile size."),
8102 { { "tile_off.png", _L("Off") }, { "tile_on.png", _L("On") } });
8103 ImGui::SameLine();
8104 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w));
8105 const wxString tile_na = layer.tile_enabled ? wxString() : _L("Turn Tile on to choose how the texture repeats.");
8106 if (icon_toggle(840, "texture_displacement_tile_repeat.svg", layer.tile_method == static_cast<TextureTileMethod>(0),
8107 icon_sm, _L("Repeat"), _L("Repeat"), { { "tile_repeat.png" } }, tile_na)) {
8108 layer.tile_method = static_cast<TextureTileMethod>(0);
8110 }
8111 ImGui::SameLine(0.f, gap_s);
8112 if (icon_toggle(841, "menu_mirror_x.svg", layer.tile_method == static_cast<TextureTileMethod>(1), icon_sm,
8113 _L("Mirrored repeat"), _L("Mirrored repeat - every other tile is flipped, so edges meet seamlessly"),
8114 { { "tile_mirror.png" } }, tile_na)) {
8115 layer.tile_method = static_cast<TextureTileMethod>(1);
8117 }
8118 }
8119
8120 // Blend, and placement on the model.
8121 {
8122 const float x0 = ImGui::GetCursorPosX();
8123 ImGui::AlignTextToFramePadding();
8124 ImGui::TextDisabled("%s", _u8L("Blend").c_str());
8125 ImGui::SameLine();
8126 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w));
8127 const std::string blend_add = _u8L_CONTEXT("Add", "Texture Displacement");
8128 const std::string blend_override = _u8L_CONTEXT("Override", "Texture Displacement");
8129 const std::string blend_subtract = _u8L_CONTEXT("Subtract", "Texture Displacement");
8130 const std::string blend_multiply = _u8L_CONTEXT("Multiply", "Texture Displacement");
8131 const std::string blend_divide = _u8L("Divide");
8132 const char *blend_items[] = { blend_add.c_str(), blend_override.c_str(), blend_subtract.c_str(),
8133 blend_multiply.c_str(), blend_divide.c_str() };
8134 // The combo's order is the panel's, not the enum's - Override reads as the sibling of Add and
8135 // belongs next to it, while TextureBlendMode's numbering is fixed by what projects already
8136 // store. Hence the explicit mapping rather than a cast of the index.
8140 static_assert(IM_ARRAYSIZE(blend_items) == IM_ARRAYSIZE(blend_modes),
8141 "every Blend entry needs its mode");
8142 // The first layer has nothing before it to combine with - build_texture_displacement() makes it
8143 // add regardless - so its combo shows Add and cannot be changed.
8144 const bool base_layer = li == 0;
8145 int blend_mode = 0;
8146 if (!base_layer)
8147 for (int bi = 0; bi < int(IM_ARRAYSIZE(blend_modes)); ++bi)
8148 if (blend_modes[bi] == layer.blend_mode)
8149 blend_mode = bi;
8150 ImGui::SetNextItemWidth(-(card_pad + gap_s + icon_sm));
8151 m_imgui->disabled_begin(base_layer);
8152 if (scoped_combo("##blend_mode", &blend_mode, blend_items, IM_ARRAYSIZE(blend_items)) && !base_layer &&
8153 blend_mode >= 0 && blend_mode < int(IM_ARRAYSIZE(blend_modes))) {
8154 layer.blend_mode = blend_modes[blend_mode];
8156 }
8157 m_imgui->disabled_end();
8158 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
8159 panel_tooltip(base_layer ?
8160 _u8L("The base layer has nothing beneath it to combine with, so it always adds.") :
8161 _u8L("What this layer does where it overlaps the ones below. Add and "
8162 "Subtract pile relief on or carve it away. Override replaces them, "
8163 "so only this layer's relief is left where it is painted. Multiply "
8164 "and Divide scale what is underneath, which turns this layer into a "
8165 "mask over it - there, Depth acts as a strength, and 1 mm leaves "
8166 "white areas untouched."),
8167 base_layer ? std::vector<TipMedia>{} :
8168 std::vector<TipMedia>{ { "blend_add.png", from_u8(blend_add) },
8169 { "blend_multiply.png", from_u8(blend_multiply) } });
8170
8171 ImGui::SameLine(0.f, gap_s);
8172 if (icon_toggle(842, "texture_displacement_adjust.svg", m_adjust_texture_mode, icon_sm, _L("Adjust placement"),
8173 _L("Adjust placement - drag the handle on the model to move the texture"))) {
8174 if (!m_adjust_texture_mode) {
8175 update_model_object(); // flush any pending strokes before anchoring
8177 m_adjust_texture_mode = true;
8178 else
8179 show_error(nullptr, _u8L("Paint something with this layer first."));
8180 } else {
8181 m_adjust_texture_mode = false;
8183 }
8184 }
8185 }
8186 }
8187
8188 // "More settings" / "Fewer settings", drawn as a link.
8189 {
8190 const bool expanded = m_layer_expanded[size_t(layer.slot)];
8191 const std::string text = expanded ? _u8L("Fewer settings") : _u8L("More settings");
8192 const float arrow = std::round(ImGui::GetFontSize() * 0.55f);
8193 const ImVec2 ts = ImGui::CalcTextSize(text.c_str());
8194 const ImVec2 p = ImGui::GetCursorScreenPos();
8195 if (ImGui::InvisibleButton("##more", ImVec2(arrow + gap_s + ts.x, ts.y))) {
8196 m_layer_expanded[size_t(layer.slot)] = !expanded;
8197 // Placement is one of the settings being hidden, so it stops with them.
8198 if (expanded && m_adjust_texture_mode) {
8199 m_adjust_texture_mode = false;
8201 }
8202 }
8203 const ImU32 c = ImGui::GetColorU32(col_link);
8204 const float cx = p.x + 0.5f * arrow, cy = p.y + 0.5f * ts.y, hs = 0.3f * arrow;
8205 if (expanded)
8206 dl->AddTriangleFilled(ImVec2(cx - hs, cy + 0.5f * hs), ImVec2(cx + hs, cy + 0.5f * hs), ImVec2(cx, cy - 0.5f * hs), c);
8207 else
8208 dl->AddTriangleFilled(ImVec2(cx - hs, cy - 0.5f * hs), ImVec2(cx + hs, cy - 0.5f * hs), ImVec2(cx, cy + 0.5f * hs), c);
8209 const float tx = p.x + arrow + gap_s;
8210 dl->AddText(ImVec2(tx, p.y), c, text.c_str());
8211 if (ImGui::IsItemHovered())
8212 dl->AddLine(ImVec2(tx, p.y + ts.y), ImVec2(tx + ts.x, p.y + ts.y), c);
8213 }
8214
8215 ImGui::EndGroup();
8216 ImGui::Unindent(card_pad);
8217 const float card_max_y = ImGui::GetItemRectMax().y + card_pad;
8218 drop_target(li, layer.slot, card_min, ImVec2(content_rx, card_max_y));
8219 if (layer.slot == dragged_slot)
8220 dl->AddRectFilled(card_min, ImVec2(content_rx, card_max_y), ghost_col, rounding);
8221 dl->ChannelsSetCurrent(0);
8222 dl->AddRectFilled(card_min, ImVec2(content_rx, card_max_y), col_card, rounding);
8223 dl->ChannelsMerge();
8224 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + card_pad);
8225 } else {
8226 // A closed layer: one outlined row - grip, chevron, thumbnail, name, depth, remove. A click
8227 // anywhere that is not one of its buttons opens it.
8228 const float inner = std::round(m_imgui->scaled(0.15f));
8229 const float row_h = frame_h + 2.f * inner;
8230 const ImVec2 p0 = ImGui::GetCursorScreenPos();
8231 const float row_w = content_rx - p0.x;
8232 if (ImGui::InvisibleButton("##row", ImVec2(row_w, row_h)))
8233 activate_slot = layer.slot;
8234 const bool row_hovered = ImGui::IsItemHovered();
8235 ImGui::SetItemAllowOverlap(); // the row's own buttons, submitted after it, still take their clicks
8236 drop_target(li, layer.slot, p0, ImVec2(p0.x + row_w, p0.y + row_h));
8237 if (row_hovered)
8238 dl->AddRectFilled(p0, ImVec2(p0.x + row_w, p0.y + row_h), col_card, rounding);
8239 dl->AddRect(p0, ImVec2(p0.x + row_w, p0.y + row_h), col_line, rounding);
8240
8241 ImGui::SetCursorScreenPos(ImVec2(p0.x + inner, p0.y + inner));
8242 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(gap_s, style.ItemSpacing.y));
8243 grip(layer, frame_h);
8244 ImGui::SameLine();
8245 {
8246 const ImVec2 c = ImGui::GetCursorScreenPos();
8247 const float aw = std::round(ImGui::GetFontSize() * 0.5f), hs = 0.3f * aw;
8248 const float cx = c.x + 0.5f * aw, cy = c.y + 0.5f * frame_h;
8249 dl->AddTriangleFilled(ImVec2(cx - 0.5f * hs, cy - hs), ImVec2(cx - 0.5f * hs, cy + hs), ImVec2(cx + 0.5f * hs, cy),
8250 ImGui::GetColorU32(ImGuiCol_TextDisabled));
8251 ImGui::Dummy(ImVec2(aw, frame_h));
8252 }
8253 ImGui::SameLine();
8254 const float th = std::round(frame_h * 0.9f);
8255 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 0.5f * (frame_h - th));
8256 if (GLTexture *thumb = get_layer_thumbnail(layer)) {
8257 if (ImGui::ImageButton((ImTextureID) (intptr_t) thumb->get_id(), ImVec2(th, th), ImVec2(0.f, 0.f), ImVec2(1.f, 1.f), 0))
8258 open_picker(layer.slot);
8259 } else if (ImGui::Button("?##thumb", ImVec2(th, th))) {
8260 open_picker(layer.slot);
8261 }
8262 hover_tip(_u8L("Pick a different image for this layer. The paint, depth and tiling stay as they are."));
8263 ImGui::SameLine();
8264
8265 char depth_text[32];
8266 std::snprintf(depth_text, sizeof(depth_text), "%.3f mm", layer.depth_mm);
8267 const std::string summary = painted ? std::string(depth_text) : _u8L("not painted");
8268 const float sum_w = ImGui::CalcTextSize(summary.c_str()).x;
8269 const float right_edge = ImGui::GetWindowContentRegionMax().x - inner;
8270 const float name_w = std::max(0.f, right_edge - ImGui::GetCursorPosX() - 2.f * gap_s - sum_w - frame_h);
8271 ImGui::SetCursorPosY(p0.y - ImGui::GetWindowPos().y + ImGui::GetScrollY() + inner);
8272 ImGui::AlignTextToFramePadding();
8273 ImGui::TextUnformatted(ellipsize(layer_name(layer), name_w).c_str());
8274 ImGui::SameLine();
8275 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), right_edge - frame_h - gap_s - sum_w));
8276 ImGui::TextDisabled("%s", summary.c_str());
8277 ImGui::SameLine();
8278 ImGui::SetCursorPosX(right_edge - frame_h);
8279 m_imgui->disabled_begin(busy);
8280 if (icon_button(600 + layer.slot, "texture_displacement_cross.svg", frame_h, m_desc.at("remove_layer"), _L("Remove this layer")))
8281 slot_to_remove = layer.slot;
8282 m_imgui->disabled_end();
8283 ImGui::PopStyleVar();
8284 if (layer.slot == dragged_slot)
8285 dl->AddRectFilled(p0, ImVec2(p0.x + row_w, p0.y + row_h), ghost_col, rounding);
8286 ImGui::SetCursorScreenPos(ImVec2(p0.x, p0.y + row_h + 0.5f * style.ItemSpacing.y));
8287 }
8288
8289 ImGui::PopID();
8290 }
8291
8292 // The dragged layer follows the cursor as a copy of its row, held by its grip. On the foreground draw list, so
8293 // it stays visible over the rest of the panel and anywhere the drag wanders.
8294 if (dragged_slot >= 0) {
8295 const auto it = std::find_if(ordered.begin(), ordered.end(), [dragged_slot](const auto *l) { return l->slot == dragged_slot; });
8296 if (it != ordered.end()) {
8297 const TextureDisplacementLayer &l = **it;
8298 ImDrawList *fg = ImGui::GetForegroundDrawList();
8299 const float inner = std::round(m_imgui->scaled(0.15f));
8300 const float row_h = frame_h + 2.f * inner;
8301 const float list_x = ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMin().x;
8302 const ImVec2 mouse = ImGui::GetIO().MousePos;
8303 const ImVec2 mn(mouse.x - inner - 0.5f * grip_w, mouse.y - 0.5f * row_h);
8304 const ImVec2 mx(mn.x + (content_rx - list_x), mn.y + row_h);
8305 fg->AddRectFilled(ImVec2(mn.x + 2.f, mn.y + 4.f), ImVec2(mx.x + 2.f, mx.y + 4.f), IM_COL32(0, 0, 0, 70), rounding);
8306 fg->AddRectFilled(mn, mx, ImGui::GetColorU32(ImGuiCol_WindowBg, 245.f / 255.f), rounding);
8307 fg->AddRect(mn, mx, ImGui::GetColorU32(orca), rounding, 0, 1.5f);
8308
8309 float x = mn.x + inner;
8310 draw_drag_icon(fg, x, mn.y + inner, grip_w, frame_h, drag_tint(255));
8311 x += grip_w + gap_s;
8312 const float th = std::round(frame_h * 0.9f);
8313 if (GLTexture *thumb = get_layer_thumbnail(l)) {
8314 const float ty = mn.y + 0.5f * (row_h - th);
8315 fg->AddImage((ImTextureID) (intptr_t) thumb->get_id(), ImVec2(x, ty), ImVec2(x + th, ty + th));
8316 }
8317 x += th + gap_s;
8318
8319 char depth_text[32];
8320 std::snprintf(depth_text, sizeof(depth_text), "%.3f mm", l.depth_mm);
8321 const std::string summary = slot_painted(l.slot) ? std::string(depth_text) : _u8L("not painted");
8322 const float sum_w = ImGui::CalcTextSize(summary.c_str()).x;
8323 const float text_y = mn.y + 0.5f * (row_h - ImGui::GetFontSize());
8324 fg->AddText(ImVec2(mx.x - inner - sum_w, text_y), ImGui::GetColorU32(ImGuiCol_TextDisabled), summary.c_str());
8325 const std::string name = ellipsize(layer_name(l), std::max(0.f, mx.x - inner - sum_w - gap_s - x));
8326 fg->AddText(ImVec2(x, text_y), ImGui::GetColorU32(ImGuiCol_Text), name.c_str());
8327 }
8328 }
8329
8330 // ---- Mesh preparation: Pro only. Standard pins all of it and runs it from Bake. ----
8331 if (pro_mode()) {
8332 ImGui::Separator();
8333 heading(_L("Subdivision"));
8334
8335 if (m_imgui->bbl_checkbox(_L("Only painted area (adaptive)"), m_subdivide_adaptive)) {
8337 rebuild_subdivide_preview(); // switch the wireframe between the uniform and adaptive result
8338 m_parent.set_as_dirty();
8339 }
8340 hover_tip(_u8L("Adds triangles only where you painted, instead of everywhere. On a large part with a small "
8341 "decal that is the difference between thousands of triangles and millions, and your paint "
8342 "survives the refinement."),
8343 { { "painted_only_off.png", _L("Off") }, { "painted_only_on.png", _L("On") } });
8344
8346 if (m_subdivide_target_mm <= 0.f && mv != nullptr) {
8347 // Seed the target at about half the mesh's mean edge length, so the default already adds a
8348 // useful amount of detail rather than landing on "no change".
8349 const indexed_triangle_set &its = mv->mesh().its;
8350 double sum = 0.0;
8351 size_t cnt = 0;
8352 for (const stl_triangle_vertex_indices &tri : its.indices)
8353 for (int i = 0; i < 3; ++i) {
8354 sum += (its.vertices[tri[i]] - its.vertices[tri[(i + 1) % 3]]).norm();
8355 ++cnt;
8356 }
8357 m_subdivide_target_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)) * 0.5f, 0.001f, 20.f) : 1.f;
8358 }
8359 // Live preview: rebuild the wireframe as the slider moves, not only on release. The rebuild is
8360 // bounded by the painted region, so it stays responsive.
8361 const auto preview_live = [this]() {
8364 m_parent.set_as_dirty();
8365 };
8366
8367 if (m_imgui->bbl_checkbox(_L("Follow texture detail"), m_subdivide_feature))
8368 preview_live();
8369 hover_tip(_u8L("Spends the triangles where the texture actually bends - packed along ridges and edges, "
8370 "sparse over flat ground - instead of spreading them evenly. The same detail for fewer "
8371 "triangles on most textures."),
8372 { { "adaptive_off.png", _L("Off") }, { "adaptive_on.png", _L("On") } });
8373
8374 // The edge-length target is a baseline in both modes. In feature mode it is what guarantees the
8375 // curvature test can actually see the texture: left too coarse, a big triangle over a fine
8376 // pattern can sample four points that all happen to land at similar heights, report no error,
8377 // and stall before refinement ever starts.
8378 if (float_row("##subdiv_target", m_subdivide_feature ? _L("Max edge") : _L("Target edge"), &m_subdivide_target_mm,
8379 0.001f, 20.f, "%.3f mm", true, 0.f,
8381 _L("No triangle in the painted area stays larger than this, even where the texture "
8382 "is flat. Keep it near the size of the smallest feature you want to come out.") :
8383 _L("Triangles in the painted area are split until none is larger than this. Smaller "
8384 "means finer detail and, fast, a lot more triangles.")))
8385 preview_live();
8386
8387 if (m_subdivide_feature) {
8388 if (float_row("##subdiv_detail", _L("Detail"), &m_subdivide_detail_mm, 0.001f, 1.f, "%.3f mm", true, 0.f,
8389 _L("How far the mesh may sit from the shape the texture describes, in millimetres. "
8390 "Smaller follows fine detail and costs triangles; larger only chases the big "
8391 "features.")))
8392 preview_live();
8393 if (float_row("##subdiv_min", _L("Min edge"), &m_subdivide_min_edge_mm, 0.001f, 20.f, "%.3f mm", true, 0.f,
8394 _L("A floor on triangle size, so a sharp step in the texture cannot be chased "
8395 "forever. Lower it for finer relief, raise it if the count runs away at hard "
8396 "edges.")))
8397 preview_live();
8398 }
8399
8400 // Applies in both adaptive sub-modes: it is about the step the bake puts at the paint's edge, which
8401 // exists whether or not "Follow texture detail" is on. 0 turns the band off.
8402 if (float_row("##subdiv_border", _L("Edge detail"), &m_subdivide_border_mm, 0.f, 5.f, "%.3f mm", false, 0.f,
8403 _L("Triangle size along the outline of the painted area, where the relief drops back to the "
8404 "bare surface. Lower it if that rim looks jagged; it only costs triangles along the "
8405 "outline. 0 turns it off.")))
8406 preview_live();
8407
8408 // Only worth showing when a layer is actually colouring: with no colour there is no boundary for it
8409 // to refine and the control would do nothing whatever it is set to.
8410 if (mv != nullptr && any_layer_colors(*mv)) {
8411 if (float_row("##subdiv_color", _L("Color detail"), &m_subdivide_color_mm, 0.f, 5.f, "%.3f mm", false, 0.f,
8412 _L("Triangle size where two colors meet. Each triangle prints in one filament, so a "
8413 "color edge can only be as sharp as the triangles along it - and nothing else "
8414 "refines there, since the surface is flat across a change of color. 0 turns it off.")))
8415 preview_live();
8416 }
8417
8418 ImGui::TextDisabled("%s", _u8L("Triangle budget: set with Triangles, below.").c_str());
8420 m_imgui->text(Slic3r::format(into_u8(_L_PLURAL("Preview: %1% triangle", "Preview: %1% triangles", m_subdivide_preview_tris)), m_subdivide_preview_tris));
8421 } else {
8422 if (int_row("##subdiv_steps", _L("Steps"), &m_subdivide_count, 0, 5, "%d", 0.f,
8423 _L("Splits every triangle of the model into four, this many times over. Each step "
8424 "quadruples the count - 3 turns 100 k triangles into 6.4 M - so prefer refining "
8425 "only the painted area unless you need the whole model finer."),
8426 { { "subdivide.gif" } })) {
8428 rebuild_subdivide_preview(); // a count of 0 clears the preview, it doesn't compute one
8429 m_parent.set_as_dirty();
8430 }
8431 }
8432
8433 {
8434 // Subdivide is a no-op when there is nothing to commit: 0 uniform passes, or an adaptive target that
8435 // is not set.
8436 const bool subdivide_ready = m_subdivide_adaptive ? (m_subdivide_target_mm > 0.f) : (m_subdivide_count >= 1);
8437 const float half = std::floor((ImGui::GetContentRegionAvail().x - style.ItemSpacing.x) * 0.5f);
8438 m_imgui->disabled_begin(busy);
8439 if (ImGui::Button((m_subdivide_editing ? _u8L("Hide preview") : _u8L("Preview subdivision")).c_str(), ImVec2(half, 0.f))) {
8440 if (m_subdivide_editing) {
8441 m_subdivide_editing = false;
8444 } else {
8445 m_subdivide_editing = true;
8447 }
8448 m_parent.set_as_dirty();
8449 }
8450 m_imgui->disabled_end();
8451 hover_tip(_u8L("Shows what the refinement would produce as a wireframe, without touching the model."));
8452 ImGui::SameLine();
8453 m_imgui->disabled_begin(!subdivide_ready || busy);
8454 if (ImGui::Button(_u8L("Subdivide").c_str(), ImVec2(half, 0.f))) {
8456 subdivide_model_adaptive(); // refines only the painted area, carrying the paint forward
8457 } else {
8458 subdivide_model(); // commits m_subdivide_count passes (takes its own snapshot)
8459 // Back to 0 rather than staying at the count just applied: the mesh is now up to 4^N times
8460 // denser, so re-previewing the same N passes on top of it is both pointless and by far the
8461 // slowest thing this panel does.
8463 }
8466 m_parent.set_as_dirty();
8467 }
8468 m_imgui->disabled_end();
8469 hover_tip(m_subdivide_adaptive ?
8470 _u8L("Refines the painted area to the target edge length and carries your paint onto "
8471 "the finer mesh. The rest of the model is left as it is.") :
8472 _u8L("Replaces the model's geometry with the subdivided mesh and clears any not-yet-baked "
8473 "paint on it (already-baked relief is unaffected)."));
8474 }
8475
8476 // Remesh: even out uneven triangle sizes (CGAL isotropic remeshing).
8477 ImGui::Separator();
8478 heading(_L("Remeshing"));
8479 if (m_remesh_target_edge_mm <= 0.f && mv != nullptr) {
8480 // Seed the target with the model's current mean edge length, so the default is a sensible "make
8481 // everything about the size it already averages".
8482 const indexed_triangle_set &its = mv->mesh().its;
8483 double sum = 0.0;
8484 size_t cnt = 0;
8485 for (const stl_triangle_vertex_indices &tri : its.indices)
8486 for (int i = 0; i < 3; ++i) {
8487 sum += (its.vertices[tri[i]] - its.vertices[tri[(i + 1) % 3]]).norm();
8488 ++cnt;
8489 }
8490 m_remesh_target_edge_mm = cnt > 0 ? std::clamp(float(sum / double(cnt)), 0.1f, 20.f) : 1.f;
8491 }
8492 float_row("##remesh_edge", _L("Target edge"), &m_remesh_target_edge_mm, 0.1f, 20.f, "%.2f mm", true, 0.f,
8493 _L("Triangle size the whole model is rebuilt with, in millimetres. Displacement works best on an "
8494 "even mesh; this is what makes one out of an uneven import."),
8495 { { "remesh_before.png", _L("Before") }, { "remesh_after.png", _L("After") } });
8496
8497 m_imgui->bbl_checkbox(_L("Keep sharp edges") + "##remesh_sharp", m_remesh_keep_sharp_edges);
8498 hover_tip(_u8L("Holds hard edges and open borders in place while the rest is remeshed. Without it "
8499 "the remesher slides vertices along the surface and rounds every crisp edge off - "
8500 "a cube comes back with wobbly edges."),
8501 { { "preserve_edges_off.png", _L("Off") }, { "preserve_edges_on.png", _L("On") } });
8502 ImGui::SameLine();
8503 m_imgui->disabled_begin(!m_remesh_keep_sharp_edges);
8504 typed_slider("##remesh_sharp_angle", -(1.f + edit_icon_room), &m_remesh_sharp_angle_deg, 5.f, 90.f, "%.0f°", false,
8505 _L("How sharp a fold has to be, in degrees, before the remesher treats it as an edge worth "
8506 "keeping. Lower protects more of the model's shape; higher rebuilds more of it evenly."));
8507 m_imgui->disabled_end();
8508
8509 m_imgui->disabled_begin(mv == nullptr || busy);
8510 if (ImGui::Button(_u8L("Remesh").c_str()))
8511 remesh_model();
8512 m_imgui->disabled_end();
8513 hover_tip(_u8L("Rebuilds the whole model with triangles close to this edge length - splitting the big "
8514 "ones and merging the small ones - so displacement has an even density to work with. "
8515 "Replaces the geometry; your paint is carried onto the new triangles spatially, so it "
8516 "survives (already-baked relief is kept too)."),
8517 { { "remesh_before.png", _L("Before") }, { "remesh_after.png", _L("After") } });
8518
8519 // Settings for the whole stack rather than one layer. Standard mode pins them instead of showing them.
8520 if (mv != nullptr) {
8521 TextureDisplacementOptions &opts = mv->texture_displacement_options;
8522 ImGui::Separator();
8523 heading(_L("Result"));
8524
8525 m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Displace up to the border"), opts.displace_border);
8526 hover_tip(_u8L("Lets the relief run right to the edge of the painted area. Turn it off to hold that "
8527 "outer ring flat, which keeps the displacement strictly inside your paint but flattens "
8528 "the pattern at the border."));
8529
8530 m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Smooth result"), opts.smooth_enabled);
8531 hover_tip(_u8L("Smooths the geometry after the texture has been applied, to take the hard steps out of a "
8532 "low-resolution image. Only what the displacement moved is touched. The Smoothing slider "
8533 "on a layer is a different thing: it blurs the image before it is used."),
8534 { { "geometry_smoothing.gif" } });
8535 if (opts.smooth_enabled) {
8536 float percent = opts.smooth_strength * 100.f;
8537 if (float_row("##dispsmooth", _L_CONTEXT("Strength", "Texture Displacement"), &percent, 1.f, 100.f, "%.0f %%", false, 0.f,
8538 _L("How far each pass pulls a vertex towards its neighbours. High values round the "
8539 "relief off quickly; low values need more passes but keep more of the detail."))) {
8540 opts.smooth_strength = std::clamp(percent / 100.f, 0.01f, 1.f);
8542 }
8543 if (int_row("##dispsmoothit", _L("Passes"), &opts.smooth_iterations, 1, 10, "%d", 0.f,
8544 _L("How many smoothing passes to run. More passes spread the smoothing further across "
8545 "the surface; Strength decides how much each one moves.")))
8547
8548 m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Ignore outer ring"), opts.smooth_skip_border);
8549 hover_tip(_u8L("Keeps the outer ring of the painted area out of the smoothing. Its neighbours "
8550 "outside the paint never move, so smoothing it drags the relief down and leaves the "
8551 "pattern half-melted at the border."));
8552
8553 // The settings above ride along with Preview/Bake. This button is for geometry that has *already*
8554 // been baked, where there is no displacement left to fold the smoothing into.
8555 if (m_imgui->button(_u8L("Smooth baked mesh now")))
8556 smooth_model();
8557 hover_tip(_u8L("Applies the smoothing above to the model itself, right now, within the painted area. "
8558 "For relief that is already baked in - anything not yet baked is smoothed by Bake."));
8559 }
8560 }
8561 }
8562
8563 // Shown in both modes. The pipeline switch, the two v2 experiments and the stage debugger are
8564 // developer controls: kept, but off the panel unless this is flipped.
8565 static constexpr bool SHOW_PIPELINE_DEV_CONTROLS = false;
8566 if (mv != nullptr) {
8567 TextureDisplacementOptions &opts = mv->texture_displacement_options;
8568 ImGui::Separator();
8569 if (SHOW_PIPELINE_DEV_CONTROLS) {
8570 // The classic path is the opt-in: the one-run pipeline is the default, and its resolution
8571 // control lives in the footer next to Bake (see below).
8572 bool classic = !opts.pipeline_v2;
8573 if (m_imgui->bbl_checkbox(_L("Experimental: classic bake pipeline"), classic)) {
8574 opts.pipeline_v2 = !classic;
8576 }
8577 hover_tip(_u8L("Bake by moving the vertices the mesh already has, after preparing it (remesh, "
8578 "adaptive subdivision, and a cut along sharp steps in the texture). Keeps the "
8579 "topology, which is what colors need. The default pipeline instead refines, "
8580 "cleans up sliver triangles, displaces and simplifies in one run; nothing needs "
8581 "preparing first, but it does not produce colors yet."));
8582 }
8583 if (SHOW_PIPELINE_DEV_CONTROLS && opts.pipeline_v2) {
8584 // Keeping the relief above the plate is not a checkbox: it is unconditional, in both pipelines
8585 // (see build_texture_displacement()).
8586 m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Align mesh to texture edges"), opts.v2_relocate);
8587 hover_tip(_u8L("Slide vertices sideways onto the edges in the texture before displacing them. "
8588 "Displacement can only move vertices up and down, so without this a sharp step "
8589 "in the image lands wherever the triangles happen to be and comes out as a "
8590 "staircase. Moving the vertices onto the step first gives a straight wall at the "
8591 "same triangle count."));
8592 m_preview_params_dirty |= m_imgui->bbl_checkbox(_L("Clean up slivers"), opts.v2_regularize);
8593 hover_tip(_u8L("Collapse the thin triangles refinement inherits from the model's own "
8594 "tessellation, before displacement samples them. A sliver's three corners "
8595 "land on three unrelated parts of the texture, which is what makes the "
8596 "relief look jagged."),
8597 { { "collapse_slivers_off.png", _L("Off") }, { "collapse_slivers_on.png", _L("On") } });
8598 }
8599 // Below both pipelines' own settings, because it replays whichever of them is selected.
8600 if (SHOW_PIPELINE_DEV_CONTROLS)
8602 }
8603
8604 // Content height, for next frame's body size. The cursor position is scroll-compensated, so this is the
8605 // height of everything above, whether or not it is scrolled.
8606 m_panel_body_h = ImGui::GetCursorPosY() - style.ItemSpacing.y;
8607 ImGui::EndChild();
8609
8610 // The layer-list changes deferred from the loop above.
8611 if (slot_to_remove >= 0)
8612 remove_texture_layer(slot_to_remove);
8613 else if (move_slot >= 0)
8614 move_texture_layer(move_slot, move_to);
8615 else if (activate_slot >= 0)
8616 set_active_layer(activate_slot);
8617 // Retexturing a layer makes it the one being painted, too.
8620
8621 // In this window's scope, where the popup's open request and the popup itself share one ID stack.
8622 ImVec2 panel_rect_min, panel_rect_max;
8623 {
8624 const ImVec2 win_min = ImGui::GetWindowPos();
8625 const ImVec2 win_sz = ImGui::GetWindowSize();
8626 render_texture_library_popup(win_min, ImVec2(win_min.x + win_sz.x, win_min.y + win_sz.y));
8627 // Only recorded here. The image-adjustment panel is a top-level window rather than a popup, so it
8628 // cannot be begun inside this one; it is drawn after GizmoImguiEnd() below.
8629 panel_rect_min = win_min;
8630 panel_rect_max = ImVec2(win_min.x + win_sz.x, win_min.y + win_sz.y);
8631 }
8632
8633 // ImGui sliders report "changed" continuously on every frame while being dragged, not just once on
8634 // release - rebuilding the preview (a real CPU mesh recompute) on every one of those frames is what made
8635 // dragging these sliders feel slow. Only rebuild once the mouse button that's driving the drag is
8636 // released, i.e. once per edit instead of dozens of times per drag.
8637 // The feature-adaptive subdivision follows the *displaced* surface (it samples depth * texture), so depth /
8638 // tile-size / rotation / invert all change where it puts triangles. The heavy displacement preview below
8639 // only rebuilds on release, which made the subdivide wireframe look like it ignored those edits - so
8640 // rebuild it live here (during the drag), the same cadence its own sliders use. Cheap: it is bounded by
8641 // the painted region.
8644 if (m_preview_params_dirty && (m_auto_update || !ImGui::IsMouseDown(ImGuiMouseButton_Left))) {
8646 // Same edits (tile size, rotation, offset, a new texture) are what the projector window draws, so it
8647 // refreshes on the same one-per-edit cadence. No-op while it is closed.
8649 m_preview_params_dirty = false;
8650 }
8651
8652 // ---- Footer: pinned below the body, so Bake never scrolls away. ----
8653 const float footer_top = ImGui::GetCursorScreenPos().y;
8654 {
8655 TextureDisplacementOptions &opts = mv->texture_displacement_options;
8656 const float x0 = ImGui::GetCursorPosX();
8657 // Over every part the bake will act on, not just the one holding the layer stack, so the count
8658 // the footer quotes is the count Bake is actually working with on a multibody object. Before
8659 // anything is painted there is nothing to bake yet, and the whole object is the honest estimate.
8660 size_t base_facets = 0;
8661 {
8662 std::vector<ModelVolume *> counted = painted_parts();
8663 if (counted.empty())
8664 counted = texture_parts();
8665 for (const ModelVolume *part : counted)
8666 base_facets += part->mesh().facets_count();
8667 }
8668 const int base_k = int((base_facets + 500) / 1000);
8669 const bool v2 = mv != nullptr && opts.pipeline_v2;
8670
8671 ImGui::Separator();
8672
8673 if (!m_show_resolution)
8674 ImGui::SetNextTreeNodeOpen(false);
8675 ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_FramePadding;
8676 ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, 0); // removes extra space on tree node indentation
8677 bool auto_res = opts.v2_refine_mm <= 0.f;
8678 std::string quality_title = (auto_res ? _u8L("Quality") + " (" + _u8L("Auto") + ")"
8679 : _u8L("Quality")) // resolution value can be shown while collapsed
8680 + "###v2quality"; // stable ID, label can change freely
8681 m_imgui->push_bold_font();
8682 const bool quality_open = ImGui::TreeNodeEx(quality_title.c_str(), flags);
8683 m_imgui->pop_bold_font();
8684 if (quality_open) {
8685 if (!m_show_resolution) {
8686 m_show_resolution = true;
8687 m_imgui->set_requires_extra_frame();
8688 } else {
8689 // The triangle budget is the one subdivision control Standard mode keeps: its right value depends on
8690 // the part rather than on the recipe (a big model, or a fine texture, simply needs more of them), and
8691 // raising or lowering it is safe without understanding anything else. Pro's Subdivide uses it too.
8692 ImGui::AlignTextToFramePadding();
8693 if (v2) {
8694 const V2Resolution &rec = v2_recommendation(*mv);
8695
8696 // -1 is "auto": the row shows the recommendation, greyed; editing it makes it a fixed value.
8697 const bool auto_budget = opts.v2_max_triangles_k < 0;
8698 int shown_k = auto_budget ? v2_recommendation(*mv).budget_k : opts.v2_max_triangles_k;
8699 m_imgui->disabled_begin(auto_budget);
8700 // TRN Slider value: %d is the triangle budget in thousands
8701 if (int_row("##v2budget", _L("Budget"), &shown_k, 0, 4000, auto_budget ? _u8L("%d k (auto)").c_str() : "%d k", 0.f,
8702 _L("How many thousand triangles this bake may spend on the area you painted. "
8703 "Relief already baked into the rest of the model is kept on top of it, so a "
8704 "second bake somewhere else gets the same budget as the first. Raise it if the "
8705 "warning below Resolution says the detail will not fit; 0 keeps every triangle "
8706 "the refinement produced, however many that is."),
8707 { { "subdivide.gif" } })) {
8708 opts.v2_max_triangles_k = shown_k;
8710 }
8711 m_imgui->disabled_end();
8712
8713 // The one control the default pipeline needs: the edge length its refinement targets, which
8714 // decides how much of the texture the mesh can carry. About eight texels per edge is where
8715 // fine detail (mortar joints, knurl ridges) stops being lost between vertices.
8716 m_imgui->text(_L("Resolution"));
8717 ImGui::SameLine();
8718 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w));
8719 // Auto: the edge (and the budget) follow the texture and the model. Off: the slider holds
8720 // a fixed value, seeded with the recommendation so it starts from something sensible.
8721 m_imgui->disabled_begin(busy);
8722 if (m_imgui->bbl_checkbox(wxString::FromUTF8("##v2auto"), auto_res)) {
8723 if (auto_res) {
8724 opts.v2_refine_mm = 0.f;
8725 opts.v2_max_triangles_k = -1;
8726 } else {
8727 opts.v2_refine_mm = rec.edge_mm > 0.f ? rec.edge_mm : 0.3f;
8728 opts.v2_max_triangles_k = rec.budget_k > 0 ? rec.budget_k : 750;
8729 }
8731 m_parent.set_as_dirty();
8732 }
8733 m_imgui->disabled_end();
8734 hover_tip(_u8L("On: the resolution follows the size of the model and the budget is left at its "
8735 "default. Untick to set both yourself - worth doing for a texture much finer or "
8736 "much coarser than the part it sits on."));
8737 ImGui::SameLine();
8738 float shown = auto_res ? rec.edge_mm : opts.v2_refine_mm;
8739 // Held in a local: the format outlives the call that reads it, and the slider and the
8740 // range in its tooltip are then formatted the same way.
8741 const std::string v2edge_format = auto_res ? _u8L("%.2f mm (auto)") : std::string("%.2f mm");
8742 const bool v2edge_chosen = auto_res && rec.edge_mm > 0.f;
8743 const wxString v2edge_tip =
8744 v2edge_chosen ?
8745 from_u8(Slic3r::format(_u8L("Chosen from the size of the model: %1$.2f mm triangles, "
8746 "budget %2% k. Untick Auto to set them yourself."),
8747 rec.edge_mm, rec.budget_k)) :
8748 _L("How fine the mesh is made under the paint, in millimetres. It has to be "
8749 "smaller than the detail you want out of the texture - a 0.5 mm groove needs "
8750 "triangles well under 0.5 mm. Smaller costs triangles fast: halving it needs "
8751 "four times as many, and once they no longer fit the budget the bake simplifies "
8752 "back down and the detail goes with it.");
8753 m_imgui->disabled_begin(busy || auto_res);
8754 if (typed_slider("##v2edge", x0 + panel_w - ImGui::GetCursorPosX() - edit_icon_room, &shown, 0.02f, 2.f,
8755 v2edge_format.c_str(), true, v2edge_tip,
8756 v2edge_chosen ? std::vector<TipMedia>{} : std::vector<TipMedia>{ { "subdivide.gif" } })) {
8757 opts.v2_refine_mm = shown;
8759 m_parent.set_as_dirty();
8760 }
8761 m_imgui->disabled_end();
8762
8763 // What this resolution costs over what is painted, against what the budget allows. Refining
8764 // past the budget is not an error - the bake simplifies back down to it - but the result
8765 // then carries less of the texture than the resolution asks for, and the only sign of that
8766 // used to be a mesh that came out coarser than expected. Shown before the bake, so the
8767 // answer is to change a number rather than to wait out a bake and redo it.
8768 const float edge_now = auto_res ? rec.edge_mm : opts.v2_refine_mm;
8769 const int budget_k = opts.v2_max_triangles_k < 0 ? rec.budget_k : opts.v2_max_triangles_k;
8770 const size_t budget = size_t(std::max(0, budget_k)) * 1000;
8771 const size_t needed = estimated_refined_triangles(*mv, edge_now);
8772 // Only when it is clearly over: the estimate runs about 3% high where it matters and up to
8773 // a third high at coarse resolutions, where the mesh's own triangles are already near the
8774 // target, and a warning about a bake that would have fitted is worse than none.
8775 if (budget > 0 && needed > budget * 5 / 4) {
8776 // Thousands under a million: a 119 k budget shown as "0.1 M" says nothing.
8777 const auto count = [](size_t n) {
8778 return n >= 1000000 ? Slic3r::format("%1$.1f M", double(n) / 1000000.) :
8779 Slic3r::format("%1% k", (n + 500) / 1000);
8780 };
8781 // Wrapped to the panel, like the note under the buttons: unwrapped text runs past the
8782 // panel's edge and takes the window's width with it.
8783 ImGui::PushTextWrapPos(x0 + panel_w);
8784 m_imgui->warning_text(Slic3r::format(_u8L("Needs about %1% triangles, budget %2% - the bake "
8785 "will simplify back down and lose detail."),
8786 count(needed), count(budget)));
8787 ImGui::PopTextWrapPos();
8788 }
8789 } else {
8790 m_imgui->text(_L("Triangles"));
8791 ImGui::SameLine();
8792 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w));
8793 m_imgui->disabled_begin(busy);
8794 if (typed_slider_int("##subdivbudget", x0 + panel_w - ImGui::GetCursorPosX() - edit_icon_room,
8795 &m_subdivide_budget_k, 10, 2000, "+%d k", true,
8796 _L("How many thousand triangles the refinement may add to the model. The triangle "
8797 "that fits worst is always split first, so even a run that spends the lot has "
8798 "spent it where it shows most. Raise it if the relief still looks coarse."),
8799 { { "subdivide.gif" } })) {
8802 m_parent.set_as_dirty();
8803 }
8804 m_imgui->disabled_end();
8805 }
8806 }
8807 ImGui::TreePop();
8808 } else if (m_show_resolution) {
8809 m_show_resolution = false;
8810 m_imgui->set_requires_extra_frame();
8811 }
8812 ImGui::PopStyleVar(1); // ImGuiStyleVar_IndentSpacing
8813
8814 const float button_h = std::round(frame_h * 1.25f);
8815 const float button_w = std::max({
8816 ImGui::CalcTextSize(_u8L("Close").c_str()).x,
8817 ImGui::CalcTextSize(_u8L("Stop").c_str()).x,
8818 ImGui::CalcTextSize(_u8L("Preparing...").c_str()).x,
8819 ImGui::CalcTextSize(_u8L("Baking...").c_str()).x,
8820 }) + m_imgui->scaled(0.5f);
8821 const float row_y = ImGui::GetCursorPosY();
8822 const float icon_h = 21.f * scale;
8823 const float row_h = std::max(icon_h, button_h);
8824
8825 const std::vector<std::pair<wxString, wxString>> shortcut =
8830 : m_shortcuts_sliders; // the sliders are there whether or not a painting tool is
8831
8832 ImGui::SetCursorPosY(row_y + (row_h - icon_h) * .5f); // center vertically
8833 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(int(m_imgui->scaled(.5f)), style.ItemSpacing.y));
8835 ImGui::SameLine();
8836 GLGizmoUtils::render_wiki_guide_button(m_parent, scale, "https://www.orcaslicer.com/wiki/print_prepare/prepare_texture_displacement");
8837 ImGui::SameLine();
8838 GLGizmoUtils::render_video_guide_button(m_parent, scale, "https://www.youtube.com/watch?v=D7w3tG1kdvE");
8839 ImGui::PopStyleVar(1);
8840
8841 ImGui::SameLine(x0 + panel_w - button_w * 2 - style.ItemSpacing.x);
8842
8843 // Read before Close below, which releases the selection any_part_painted() finds the parts through.
8844 const bool painted = mv != nullptr && any_part_painted();
8845 const bool can_bake = !busy && painted;
8846 const std::string bake_label = m_prepare_in_progress ? _u8L("Preparing...") :
8847 m_bake_in_progress ? _u8L("Baking...") :
8848 into_u8(m_desc.at("bake"));
8850 m_imgui->push_bold_font();
8851 m_imgui->disabled_begin(!can_bake);
8852 ImGui::SetCursorPosY(row_y + (row_h - button_h) * .5f); // center vertically
8853 if (ImGui::Button((bake_label + "##bake").c_str(), ImVec2(button_w, button_h))) {
8854 // Standard mode's Bake is the whole pipeline (remesh -> refine -> displace); Pro's is only the
8855 // displacement, because there the user has already prepared the mesh with the controls above.
8856 if (pro_mode())
8857 bake();
8858 else
8859 bake_standard();
8860 }
8861 m_imgui->disabled_end();
8862 m_imgui->pop_bold_font();
8864 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled))
8865 panel_tooltip(mv != nullptr && !painted ?
8866 (m_seam_edit_mode ? _u8L("Nothing is painted yet. The UV editor's seam tool is on, so "
8867 "strokes on the model mark seams instead of painting - turn "
8868 "it off in the pane to paint.") :
8869 _u8L("Nothing is painted yet.")) :
8870 pro_mode() ?
8871 _u8L("Turn the painted height maps into real geometry, by moving the vertices that are "
8872 "already there. Use Subdivide first if the mesh is too coarse to show the detail.") :
8873 _u8L("Turn the painted height maps into real geometry. The mesh is remeshed to an even "
8874 "density and refined where the texture bends first, so the detail has vertices to "
8875 "land on - all in one step."));
8876
8877 ImGui::SameLine();
8878 if (busy) {
8879 if (ImGui::Button((_u8L("Stop") + "##stop").c_str(), ImVec2(button_w, button_h)))
8880 wxGetApp().plater()->get_ui_job_worker().cancel_all();
8881 hover_tip(_u8L("Stops the bake. Whatever it had already finished stays on the model, and can be undone."));
8882 } else {
8883 if (ImGui::Button((_u8L("Close") + "##close").c_str(), ImVec2(button_w, button_h)))
8884 m_parent.reset_all_gizmos();
8885 hover_tip(_u8L("Closes the tool without baking. Your paint, layers and settings stay with the model."));
8886 }
8887
8888 // What Bake will produce, and which layers it will skip.
8889 if (mv != nullptr) {
8890 const std::string base_count = base_k > 0 ? Slic3r::format(_u8L("%1% k"), base_k) :
8891 std::to_string(base_facets);
8892 const unsigned base_n = base_k > 0 ? unsigned(base_k) * 1000 : unsigned(base_facets);
8893 const unsigned result_n = unsigned(base_k + m_subdivide_budget_k) * 1000;
8894 // TRN %1% is a triangle count, either exact or rounded to thousands such as "120 k".
8895 std::string note = v2 ? Slic3r::format(into_u8(_L_PLURAL("Model: %1% triangle - the bake refines to the resolution above, up to its budget",
8896 "Model: %1% triangles - the bake refines to the resolution above, up to its budget", base_n)), base_count) :
8897 // TRN %1% is a triangle count, either exact or rounded to thousands such as "120 k".
8898 pro_mode() ? Slic3r::format(into_u8(_L_PLURAL("Bake moves existing vertices - the model stays at %1% triangle",
8899 "Bake moves existing vertices - the model stays at %1% triangles", base_n)), base_count) :
8900 Slic3r::format(into_u8(_L_PLURAL("Bake result up to ~%1% k triangle", "Bake result up to ~%1% k triangles", result_n)),
8901 base_k + m_subdivide_budget_k);
8902 if (painted) {
8903 std::vector<std::string> skipped;
8904 for (const TextureDisplacementLayer &l : mv->texture_displacement_layers)
8905 if (!slot_painted(l.slot))
8906 skipped.push_back(layer_name(l));
8907 if (skipped.size() > 2)
8908 note += ", " + Slic3r::format(into_u8(_L_PLURAL("%1% layer not painted, skipped", "%1% layers not painted, skipped",
8909 unsigned(skipped.size()))), skipped.size());
8910 else if (!skipped.empty())
8911 // TRN %1% is one layer name, or two names separated by a comma.
8912 note += ", " + Slic3r::format(into_u8(_L_PLURAL("%1% not painted, skipped", "%1% not painted, skipped",
8913 unsigned(skipped.size()))),
8914 skipped.size() == 2 ? skipped[0] + ", " + skipped[1] : skipped[0]);
8915 }
8916 ImGui::PushTextWrapPos(x0 + panel_w);
8917 ImGui::TextDisabled("%s", note.c_str());
8918 ImGui::PopTextWrapPos();
8919 }
8920 }
8921 m_panel_footer_h = ImGui::GetCursorScreenPos().y - footer_top;
8922 constexpr float resize_epsilon = 0.5f;
8923 const float content_bottom = ImGui::GetCursorPosY() - style.ItemSpacing.y;
8924 const float window_bottom = ImGui::GetWindowContentRegionMax().y;
8925 const bool outer_window_needs_fit = content_bottom < window_bottom - resize_epsilon ||
8926 (content_bottom > window_bottom + resize_epsilon &&
8927 ImGui::GetWindowHeight() < ImGui::GetMainViewport()->Size.y -
8928 2.f * style.DisplaySafeAreaPadding.y - resize_epsilon);
8929 if (std::abs(m_panel_body_h - previous_body_h) > resize_epsilon ||
8930 std::abs(m_panel_footer_h - previous_footer_h) > resize_epsilon || outer_window_needs_fit) {
8931 m_parent.request_extra_frame();
8932 }
8933 GizmoImguiEnd();
8934
8935 // Outside the gizmo's window, because it is a window of its own: a floating, non-modal panel that
8936 // leaves the 3D view live so the relief can be watched while the sliders move. No-op when closed.
8937 render_image_adjust_window(panel_rect_min, panel_rect_max);
8938
8940
8941 // Drawn last, over everything, via the foreground draw list: the +/- add-remove sign next to the 3D
8942 // cursor. Still inside the gizmo's ImGui frame here, which is what render_paint_cursor_hint() needs.
8944}
#define style
Definition WipeTowerDialog.cpp:101
std::string format(float value, unsigned int decimals) const
Definition GLGizmoBase.cpp:519
std::string get_name(bool include_shortcut=true) const
Definition GLGizmoBase.cpp:544
std::optional< Shortcut > shortcut() const
Definition GLGizmoBase.hpp:178
void GizmoImguiEnd()
Definition GLGizmoBase.cpp:372
bool GizmoImguiBegin(const std::string &name, int flags)
Definition GLGizmoBase.cpp:367
const V2Resolution & v2_recommendation(const ModelVolume &mv)
Definition GLGizmoTextureDisplacement.cpp:5675
void update_projector()
Definition GLGizmoTextureDisplacement.cpp:3198
int m_subdivide_preview_tris
Definition GLGizmoTextureDisplacement.hpp:778
int m_subdivide_count
Definition GLGizmoTextureDisplacement.hpp:776
void move_texture_layer(int slot, int to_index)
Definition GLGizmoTextureDisplacement.cpp:5218
bool m_picker_open_request
Definition GLGizmoTextureDisplacement.hpp:508
void subdivide_model_adaptive()
Definition GLGizmoTextureDisplacement.cpp:6283
void follow_island_highlight()
Definition GLGizmoTextureDisplacement.cpp:2887
void store_panel_state()
Definition GLGizmoTextureDisplacement.cpp:3984
bool m_picker_reveal_selected
Definition GLGizmoTextureDisplacement.hpp:510
GLModel m_subdivide_preview_glmodel
Definition GLGizmoTextureDisplacement.hpp:779
bool m_auto_update
Definition GLGizmoTextureDisplacement.hpp:765
int m_picker_slot
Definition GLGizmoTextureDisplacement.hpp:507
int m_panel_mode
Definition GLGizmoTextureDisplacement.hpp:360
void show_projector(bool show)
Definition GLGizmoTextureDisplacement.cpp:3165
void render_image_adjust_window(const ImVec2 &panel_min, const ImVec2 &panel_max)
Definition GLGizmoTextureDisplacement.cpp:4995
void process_uv_commands()
Definition GLGizmoTextureDisplacement.cpp:2837
void render_paint_cursor_hint()
Definition GLGizmoTextureDisplacement.cpp:6996
ImVec2 m_tooltip_anchor_min
Definition GLGizmoTextureDisplacement.hpp:1273
bool pro_mode() const
Definition GLGizmoTextureDisplacement.hpp:361
bool m_undocked
Definition GLGizmoTextureDisplacement.hpp:1186
void add_texture_layer()
Definition GLGizmoTextureDisplacement.cpp:4241
float m_panel_footer_h
Definition GLGizmoTextureDisplacement.hpp:1205
GLTexture * get_layer_thumbnail(const TextureDisplacementLayer &layer)
Definition GLGizmoTextureDisplacement.cpp:6540
bool m_show_resolution
Definition GLGizmoTextureDisplacement.hpp:1190
void select_whole_model()
Definition GLGizmoTextureDisplacement.cpp:5381
int apply_projection_frame()
Definition GLGizmoTextureDisplacement.cpp:3224
void panel_tooltip(const std::string &text, const std::vector< TipMedia > &media={})
Definition GLGizmoTextureDisplacement.cpp:4085
bool m_wireframe_overlay
Definition GLGizmoTextureDisplacement.hpp:1095
void bake_standard()
Definition GLGizmoTextureDisplacement.cpp:6698
float m_panel_body_h
Definition GLGizmoTextureDisplacement.hpp:1204
static constexpr const char * UNDOCKED_CONFIG_KEY
Definition GLGizmoTextureDisplacement.hpp:1188
void remesh_model()
Definition GLGizmoTextureDisplacement.cpp:6441
void remove_texture_layer(int slot)
Definition GLGizmoTextureDisplacement.cpp:5147
float m_panel_scroll_applied
Definition GLGizmoTextureDisplacement.hpp:1200
int m_projector_opacity
Definition GLGizmoTextureDisplacement.hpp:452
void render_texture_library_popup(const ImVec2 &panel_min, const ImVec2 &panel_max)
Definition GLGizmoTextureDisplacement.cpp:4390
std::string m_slider_type_request
Definition GLGizmoTextureDisplacement.hpp:525
bool m_project_only_visible
Definition GLGizmoTextureDisplacement.hpp:446
void smooth_model()
Definition GLGizmoTextureDisplacement.cpp:6310
void subdivide_model()
Definition GLGizmoTextureDisplacement.cpp:5403
float m_panel_scroll_target
Definition GLGizmoTextureDisplacement.hpp:1199
void render_debug_stage_panel(ModelVolume *mv)
Definition GLGizmoTextureDisplacement.cpp:6832
int m_slider_type_request_frame
Definition GLGizmoTextureDisplacement.hpp:526
bool m_subdivide_editing
Definition GLGizmoTextureDisplacement.hpp:777
void open_image_adjust(const TextureDisplacementLayer &layer)
Definition GLGizmoTextureDisplacement.cpp:4842
size_t estimated_refined_triangles(const ModelVolume &mv, float edge_mm)
Definition GLGizmoTextureDisplacement.cpp:5668
void ease_wheel_scroll(float &target, float &applied)
Definition GLGizmoTextureDisplacement.cpp:4356
ImVec2 m_tooltip_anchor_max
Definition GLGizmoTextureDisplacement.hpp:1274
void ensure_panel_icons()
Definition GLGizmoTextureDisplacement.cpp:4010
void rebuild_subdivide_preview()
Definition GLGizmoTextureDisplacement.cpp:6457
static void push_toolbar_style(const float scale)
Definition ImGuiWrapper.cpp:2705
static const ImVec4 COL_ORCA
Definition ImGuiWrapper.hpp:408
static void pop_combo_style()
Definition ImGuiWrapper.cpp:2932
static void pop_toolbar_style()
Definition ImGuiWrapper.cpp:2757
static void push_combo_style(const float scale)
Definition ImGuiWrapper.cpp:2907
void push_orca_button_style()
Definition GLGizmoUtils.cpp:161
bool render_wiki_guide_button(const GLCanvas3D &canvas, float scale, const char *url)
Definition GLGizmoUtils.cpp:79
void pop_orca_button_style()
Definition GLGizmoUtils.cpp:187
void render_tooltip_button(ImGuiWrapper *imgui_wrapper, const GLCanvas3D &canvas, const std::vector< std::pair< wxString, wxString > > &shortcuts, float x, float y)
Definition GLGizmoUtils.cpp:48
bool render_video_guide_button(const GLCanvas3D &canvas, float scale, const char *url)
Definition GLGizmoUtils.cpp:97
void pop_slim_scrollbar_style()
Definition GLGizmoTextureDisplacement.cpp:120
void push_slim_scrollbar_style(float width, bool dark)
Definition GLGizmoTextureDisplacement.cpp:108
std::string ellipsize(std::string s, float max_w, float font_size=0.f)
Definition GLGizmoTextureDisplacement.cpp:127
static std::string to_string(libvgcode::EMoveType type)
Definition GCodeViewer.cpp:377
int get_dpi_for_window(const wxWindow *window)
Definition GUI_Utils.cpp:234
@ DPI_DEFAULT
Definition GUI_Utils.hpp:92
wxString from_u8(const std::string &str)
Definition GUI.cpp:544
TextureBlendMode
Definition TextureDisplacement.hpp:130
@ Divide
Definition TextureDisplacement.hpp:134
@ Subtract
Definition TextureDisplacement.hpp:132
@ Override
Definition TextureDisplacement.hpp:139
@ Multiply
Definition TextureDisplacement.hpp:133
@ Add
Definition TextureDisplacement.hpp:131
int budget_k
Definition TextureDisplacement.hpp:518
TextureTileMethod
Definition TextureDisplacement.hpp:49
std::string format(const char *fmt, TArgs &&... args)
Definition format.hpp:45
TextureProjectionMethod
Definition TextureDisplacement.hpp:56
bool is_active(id view)
Definition MacIME.mm:26
@ right
Definition MarchingSquares.hpp:190
#define _u8L_CONTEXT(s, ctx)
Definition I18N.hpp:16
#define _L_PLURAL(s, plural, n)
Definition I18N.hpp:96
bool has_color() const
Definition TextureDisplacement.hpp:542

References _L, _u8L, _u8L_CONTEXT, active_layer(), apply_standard_mode_presets(), Slic3r::GUI::IconManager::Icon::br, Slic3r::GUI::GLGizmoPainterBase::BRUSH, cached_palette(), Slic3r::TriangleSelector::CIRCLE, Slic3r::GUI::ImGuiWrapper::COL_ORCA, Slic3r::GUI::GLGizmoPainterBase::CursorRadiusMax, Slic3r::GUI::GLGizmoPainterBase::CursorRadiusMin, Slic3r::GUI::DPI_DEFAULT, ease_wheel_scroll(), Slic3r::FacetsAnnotation::empty(), ensure_panel_icons(), follow_island_highlight(), Slic3r::format(), Slic3r::GUI::format(), Slic3r::GUI::from_u8(), Slic3r::GUI::get_dpi_for_window(), Slic3r::GUI::GLGizmoBase::get_name(), Slic3r::UndoRedo::GizmoAction, Slic3r::GUI::GLGizmoBase::GizmoImguiBegin(), Slic3r::GUI::GLGizmoBase::GizmoImguiSetNextWIndowPos(), Slic3r::ModelVolume::is_model_part(), m_active_layer_slot, m_bake_in_progress, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoPainterBase::m_cursor_radius, Slic3r::GUI::GLGizmoPainterBase::m_cursor_type, m_desc, m_erase_mode, Slic3r::GUI::GLGizmoBase::m_imgui, m_palette_changed, m_panel_body_h, m_panel_footer_h, m_panel_icon_map, m_panel_mode, m_panel_scroll_applied, m_panel_scroll_target, Slic3r::GUI::GLGizmoBase::m_parent, m_prepare_in_progress, m_preview_params_dirty, m_slider_type_request, m_slider_type_request_frame, Slic3r::GUI::GLGizmoPainterBase::m_smart_fill_angle, m_subdivide_editing, m_subdivide_preview_glmodel, m_subdivide_preview_tris, Slic3r::GUI::GLGizmoPainterBase::m_tool_type, m_tooltip_anchor_max, m_tooltip_anchor_min, Slic3r::GUI::GLGizmoPainterBase::m_triangle_selectors, m_undocked, panel_tooltip(), Slic3r::GUI::plater(), Slic3r::TriangleSelector::POINTER, Slic3r::GUI::ImGuiWrapper::pop_combo_style(), pro_mode(), process_uv_commands(), Slic3r::GUI::ImGuiWrapper::push_combo_style(), Slic3r::GUI::ImGuiWrapper::push_toolbar_style(), Slic3r::range(), scale(), select_whole_model(), Slic3r::GUI::GLGizmoPainterBase::SMART_FILL, Slic3r::GUI::GLGizmoPainterBase::SmartFillAngleMax, Slic3r::GUI::GLGizmoPainterBase::SmartFillAngleMin, Slic3r::TriangleSelector::SPHERE, store_panel_state(), style, Slic3r::GUI::IconManager::Icon::tex_id, Slic3r::ModelVolume::texture_displacement_facet(), texture_volume(), Slic3r::GUI::IconManager::Icon::tl, UNDOCKED_CONFIG_KEY, update_model_object(), and Slic3r::ModelObject::volumes.

Here is the call graph for this function:

◆ on_save()

void Slic3r::GUI::GLGizmoPainterBase::on_save ( cereal::BinaryOutputArchive & ar) const
inlineoverrideprotectedvirtualinherited

Reimplemented from Slic3r::GUI::GLGizmoBase.

404{}

◆ on_set_hover_id()

virtual void Slic3r::GUI::GLGizmoBase::on_set_hover_id ( )
inlineprotectedvirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoBrimEars, Slic3r::GUI::GLGizmoCut3D, Slic3r::GUI::GLGizmoEmboss, Slic3r::GUI::GLGizmoRotate3D, and Slic3r::GUI::GLGizmoSVG.

245{}

Referenced by set_hover_id().

Here is the caller graph for this function:

◆ on_set_state()

void Slic3r::GUI::GLGizmoPainterBase::on_set_state ( )
overrideprotectedvirtualinherited

Reimplemented from Slic3r::GUI::GLGizmoBase.

Reimplemented in Slic3r::GUI::GLGizmoSeam.

1195{
1196 if (m_state == m_old_state)
1197 return;
1198
1199 if (m_state == On && m_old_state != On) { // the gizmo was just turned on
1200 m_parent.enable_picking(false);
1201 on_opening();
1202
1203 const Selection& selection = m_parent.get_selection();
1204 //Camera& camera = wxGetApp().plater()->get_camera();
1205 //Vec3d rotate_target = selection.get_bounding_box().center();
1206 //rotate_target(2) = 0.f;
1207 //Vec3d position = camera.get_position();
1208 //camera.set_target(rotate_target);
1209 //camera.look_at(position, rotate_target, Vec3d::UnitZ());
1210 }
1211 if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
1212 m_parent.enable_picking(true);
1213 // we are actually shutting down
1214 on_shutdown();
1215 m_old_mo_id = -1;
1216 //m_iva.release_geometry();
1217 m_triangle_selectors.clear();
1218 // A stroke still open goes with the selectors. Its state must not carry into the next session, where the
1219 // first move would end it as if released and the first press would paint a line from its last position.
1221 m_last_mouse_click = Vec2d::Zero();
1222
1223 //Camera& camera = wxGetApp().plater()->get_camera();
1224 //camera.look_at(camera.get_position(), m_previous_target, Vec3d::UnitZ());
1225 //camera.set_target(m_previous_target);
1226 //camera.recover_from_free_camera();
1227 }
1229}
EState m_old_state
Definition GLGizmoPainterBase.hpp:360

References m_button_down, m_last_mouse_click, m_old_mo_id, m_old_state, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::GUI::GLGizmoBase::m_state, m_triangle_selectors, None, Slic3r::GUI::GLGizmoBase::Off, Slic3r::GUI::GLGizmoBase::On, on_opening(), and on_shutdown().

Referenced by Slic3r::GUI::GLGizmoFdmSupports::on_set_state(), Slic3r::GUI::GLGizmoMmuSegmentation::on_set_state(), and Slic3r::GUI::GLGizmoSeam::on_set_state().

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

◆ on_shutdown()

void Slic3r::GUI::GLGizmoTextureDisplacement::on_shutdown ( )
overrideprivatevirtual

Implements Slic3r::GUI::GLGizmoPainterBase.

582{
583 m_parent.toggle_model_objects_visibility(true);
584 m_preview_glmodel.reset();
585 m_part_previews.clear();
588 m_paint_overlay_dirty = false;
590 m_painted_colors_key.clear();
593 m_painted_colors_runs.clear();
594 m_seed_fill_last_mesh_id = -1; // a hover from this session must not count in the next
595 // Any preview still in flight is superseded: raising the shared counter makes it abort at its next
596 // progress poll, and its completion handler then finds nothing to do.
597 m_preview_generation->fetch_add(1);
598 m_preview_job_pending = false;
599 // The adjustment panel holds a full-size decode of its layer's image, which is the largest thing
600 // here; a closed gizmo should not keep it alive. Discards the edit, as closing the gizmo on an open
601 // panel implies.
603 // The palette caches hold the last volume's images; a closed gizmo should not keep them alive.
604 m_palette_cache.clear();
605 m_palette_quantizer = nullptr;
606 m_palette_pure_quantizer = nullptr;
607 m_palette_filaments.clear();
608 m_palette_images.clear();
609 m_mix_ranking.reset();
610 m_palette_slots.clear();
611 m_palette_changed = false;
612 m_uvcheck_glmodel.reset();
615 m_seam_glmodel.reset();
616 m_seam_hover_glmodel.reset();
617 m_seam_hover_edge = { -1, -1 };
619 m_seam_anchor_glmodel.reset();
620 m_seam_path_mode = false;
622 m_subdivide_editing = false;
626 m_shaded_active_face.clear();
627 m_shaded_island_delta = Eigen::Matrix<float, 2, 3>::Identity();
628 m_island_drag_active = false;
629 m_island_move_set.clear();
630 // The pane closes below, taking the highlight with it, and the view must not open the next session in a
631 // Fast the user never picked. Set directly: nothing is to be queued against a gizmo that is closing.
632 if (m_island_view.switched)
633 m_use_shaded_preview = false;
635 m_adjust_texture_mode = false;
636 m_seam_edit_mode = false;
638 m_adjust_anchor_valid = false;
639 // The pane is request-only: closing the gizmo drops the request, so reopening it later doesn't
640 // silently reopen the pane too.
641 m_show_uv_editor = false;
642 wxGetApp().plater()->show_uv_editor(false);
643
644 // Destroyed, not just hidden: unlike the UV pane (owned by Plater), this frame is owned here, and
645 // it holds a callback capturing `this`. Leaving it alive past the gizmo would leave that callback
646 // pointing at a gizmo that is no longer driving anything.
647 if (m_projector_frame != nullptr) {
648 m_projector_frame->Destroy();
649 m_projector_frame = nullptr;
650 }
651 m_projector_tex_source = nullptr; // a rebuilt frame starts with no texture in it
653}
std::shared_ptr< std::atomic< uint64_t > > m_preview_generation
Definition GLGizmoTextureDisplacement.hpp:1112
GLModel m_seam_anchor_glmodel
Definition GLGizmoTextureDisplacement.hpp:735
std::string m_painted_colors_drawn_key
Definition GLGizmoTextureDisplacement.hpp:954
GLModel m_painted_colors_glmodel
Definition GLGizmoTextureDisplacement.hpp:953
std::string m_painted_colors_key
Definition GLGizmoTextureDisplacement.hpp:952
GLModel m_shaded_preview_glmodel
Definition GLGizmoTextureDisplacement.hpp:918
GLModel m_uvcheck_glmodel
Definition GLGizmoTextureDisplacement.hpp:1054
GLModel m_paint_overlay_glmodel
Definition GLGizmoTextureDisplacement.hpp:923
bool m_preview_job_pending
Definition GLGizmoTextureDisplacement.hpp:1121
const void * m_projector_tex_source
Definition GLGizmoTextureDisplacement.hpp:455
GLModel m_seam_hover_glmodel
Definition GLGizmoTextureDisplacement.hpp:726
PaintedColors m_painted_colors
Definition GLGizmoTextureDisplacement.hpp:951
float m_projector_tex_smoothing
Definition GLGizmoTextureDisplacement.hpp:456
std::vector< std::pair< int, std::pair< size_t, size_t > > > m_painted_colors_runs
Definition GLGizmoTextureDisplacement.hpp:955
GLModel m_seam_glmodel
Definition GLGizmoTextureDisplacement.hpp:713
Definition GLGizmoTextureDisplacement.hpp:251
Definition GLGizmoTextureDisplacement.hpp:235

References close_image_adjust(), m_adjust_anchor_valid, m_adjust_drag_handle, m_adjust_texture_mode, m_island_drag_active, m_island_move_set, m_island_view, m_mix_ranking, m_paint_overlay_dirty, m_paint_overlay_glmodel, m_painted_colors, m_painted_colors_drawn_key, m_painted_colors_glmodel, m_painted_colors_key, m_painted_colors_runs, m_palette_cache, m_palette_changed, m_palette_filaments, m_palette_images, m_palette_pure_quantizer, m_palette_quantizer, m_palette_slots, Slic3r::GUI::GLGizmoBase::m_parent, m_part_previews, m_preview_generation, m_preview_glmodel, m_preview_job_pending, m_projector_frame, m_projector_tex_smoothing, m_projector_tex_source, m_seam_anchor_glmodel, m_seam_edit_mode, m_seam_glmodel, m_seam_hover_edge, m_seam_hover_glmodel, m_seam_hover_vertex, m_seam_path_anchor, m_seam_path_mode, Slic3r::GUI::GLGizmoPainterBase::m_seed_fill_last_mesh_id, m_shaded_active_chart, m_shaded_active_face, m_shaded_island_delta, m_shaded_preview_glmodel, m_show_uv_editor, m_subdivide_editing, m_subdivide_preview_glmodel, m_subdivide_preview_tris, m_use_shaded_preview, m_uvcheck_glmodel, m_wireframe_overlay_glmodel, m_wireframe_overlay_vcount, and None.

Here is the call graph for this function:

◆ on_start_dragging()

virtual void Slic3r::GUI::GLGizmoBase::on_start_dragging ( )
inlineprotectedvirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoBrimEars, Slic3r::GUI::GLGizmoCut3D, Slic3r::GUI::GLGizmoEmboss, Slic3r::GUI::GLGizmoMove3D, Slic3r::GUI::GLGizmoRotate3D, Slic3r::GUI::GLGizmoRotate, Slic3r::GUI::GLGizmoScale3D, and Slic3r::GUI::GLGizmoSVG.

253{}

Referenced by use_grabbers().

Here is the caller graph for this function:

◆ on_stop_dragging()

virtual void Slic3r::GUI::GLGizmoBase::on_stop_dragging ( )
inlineprotectedvirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoBrimEars, Slic3r::GUI::GLGizmoCut3D, Slic3r::GUI::GLGizmoEmboss, Slic3r::GUI::GLGizmoMove3D, Slic3r::GUI::GLGizmoRotate3D, Slic3r::GUI::GLGizmoScale3D, and Slic3r::GUI::GLGizmoSVG.

254{}

Referenced by do_stop_dragging(), and Slic3r::GUI::GLGizmoRotate::stop_dragging().

Here is the caller graph for this function:

◆ on_tool_shortcut()

virtual bool Slic3r::GUI::GLGizmoPainterBase::on_tool_shortcut ( Shortcut shortcut)
inlinevirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoFdmSupports, Slic3r::GUI::GLGizmoMmuSegmentation, and Slic3r::GUI::GLGizmoSeam.

209{ return false; }

References Slic3r::GUI::GLGizmoBase::shortcut().

Here is the call graph for this function:

◆ on_unregister_raycasters_for_picking()

virtual void Slic3r::GUI::GLGizmoBase::on_unregister_raycasters_for_picking ( )
inlineprotectedvirtualinherited

Reimplemented in Slic3r::GUI::GLGizmoBrimEars, Slic3r::GUI::GLGizmoCut3D, Slic3r::GUI::GLGizmoEmboss, Slic3r::GUI::GLGizmoFlatten, Slic3r::GUI::GLGizmoMove3D, Slic3r::GUI::GLGizmoRotate3D, Slic3r::GUI::GLGizmoScale3D, and Slic3r::GUI::GLGizmoSVG.

268{}

Referenced by unregister_raycasters_for_picking().

Here is the caller graph for this function:

◆ on_uv_command()

void Slic3r::GUI::GLGizmoTextureDisplacement::on_uv_command ( int cmd,
float value )
private
2832{
2833 m_uv_command_queue.emplace_back(cmd, value);
2834 m_parent.set_as_dirty(); // the panel render that runs it
2835}
std::vector< std::pair< int, float > > m_uv_command_queue
Definition GLGizmoTextureDisplacement.hpp:684

References Slic3r::GUI::GLGizmoBase::m_parent, and m_uv_command_queue.

Referenced by update_uv_editor().

Here is the caller graph for this function:

◆ on_uv_vertex_edited()

void Slic3r::GUI::GLGizmoTextureDisplacement::on_uv_vertex_edited ( const std::vector< std::pair< int, Vec2f > > & edits)
private
3464{
3465 TextureDisplacementLayer *layer = active_layer();
3466 if (layer == nullptr || edits.empty() || m_uv_editor_unwrap.empty())
3467 return;
3468
3469 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Edit texture UV"), UndoRedo::SnapshotType::GizmoAction);
3470
3471 for (const auto &[unwrapped, raw_uv] : edits) {
3472 if (unwrapped < 0 || size_t(unwrapped) >= m_uv_editor_unwrap.source_vertex.size())
3473 continue;
3474 // Keep the gizmo's own unwrap copy in step, so the pane and the bake agree without a re-solve.
3475 if (size_t(unwrapped) < m_uv_editor_unwrap.uvs.size())
3476 m_uv_editor_unwrap.uvs[size_t(unwrapped)] = raw_uv;
3477
3478 // Keyed by this one unwrapped copy, not by its mesh vertex: a seam vertex has a copy in each island it
3479 // borders, and only the one that was dragged may move. Small list, linear scan is fine.
3480 const int key = lscm_uv_override_key(unwrapped);
3481 auto it = std::find_if(layer->lscm_uv_overrides.begin(), layer->lscm_uv_overrides.end(),
3482 [key](const std::pair<int, Vec2f> &p) { return p.first == key; });
3483 if (it != layer->lscm_uv_overrides.end())
3484 it->second = raw_uv;
3485 else
3486 layer->lscm_uv_overrides.emplace_back(key, raw_uv);
3487 }
3488
3489 rebuild_preview(); // the baked displacement samples the moved uv now
3490}
int lscm_uv_override_key(int unwrapped_vertex)
Definition TextureDisplacement.hpp:906

References _u8L, active_layer(), Slic3r::TextureDisplacementLayer::empty(), Slic3r::UndoRedo::GizmoAction, Slic3r::lscm_uv_override_key(), Slic3r::TextureDisplacementLayer::lscm_uv_overrides, m_uv_editor_unwrap, Slic3r::GUI::plater(), and rebuild_preview().

Referenced by update_uv_editor().

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

◆ open_image_adjust()

void Slic3r::GUI::GLGizmoTextureDisplacement::open_image_adjust ( const TextureDisplacementLayer & layer)
private
4843{
4844 if (layer.empty() || layer.slot < 0)
4845 return;
4846
4847 close_image_adjust(); // reverts and clears any previous panel
4848
4849 if (!load_image_adjust_source(layer))
4850 return;
4851
4852 m_adjust = TextureAdjustments{};
4853 m_adjust_applied = TextureAdjustments{};
4854 m_adjust_slot = layer.slot;
4855 const ModelVolume *mv = texture_volume();
4856 m_adjust_volume_id = mv != nullptr ? mv->id().id : 0;
4857}
bool load_image_adjust_source(const TextureDisplacementLayer &layer)
Definition GLGizmoTextureDisplacement.cpp:4814

References close_image_adjust(), Slic3r::TextureDisplacementLayer::empty(), Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, load_image_adjust_source(), m_adjust, m_adjust_applied, m_adjust_slot, m_adjust_volume_id, Slic3r::TextureDisplacementLayer::slot, and texture_volume().

Here is the call graph for this function:

◆ painted_area_mm2()

double Slic3r::GUI::GLGizmoTextureDisplacement::painted_area_mm2 ( const ModelVolume & mv)
private
5638{
5639 // Keyed on the paint generation, which every stroke and every layer edit raises, so a panel that
5640 // asks for this on every frame walks the mesh only when the answer can have changed.
5641 const std::string key = std::to_string(mv.id().id) + ":" + std::to_string(mv.mesh().facets_count()) + ":" +
5642 std::to_string(m_preview_generation->load());
5643 if (key == m_painted_area_key)
5644 return m_painted_area_mm2;
5645
5646 const TriangleMesh &mesh = mv.mesh();
5647 std::vector<uint8_t> region;
5648 double area = 0.;
5649 if (collect_paint_region(mesh, facets_data_of(mv), region, nullptr)) {
5650 // In the frame the bake refines in, so a scaled instance is measured at the size it prints at.
5652 const indexed_triangle_set &its = mesh.its;
5653 for (size_t t = 0; t < its.indices.size() && t < region.size(); ++t) {
5654 if ((region[t] & REFINE_PAINTED) == 0)
5655 continue;
5656 const stl_triangle_vertex_indices &tri = its.indices[t];
5657 const Vec3d a = frame * its.vertices[size_t(tri[0])].cast<double>();
5658 const Vec3d b = frame * its.vertices[size_t(tri[1])].cast<double>();
5659 const Vec3d c = frame * its.vertices[size_t(tri[2])].cast<double>();
5660 area += 0.5 * (b - a).cross(c - a).norm();
5661 }
5662 }
5664 m_painted_area_key = key;
5665 return area;
5666}
double m_painted_area_mm2
Definition GLGizmoTextureDisplacement.hpp:876
static TextureDisplacementFacetsData facets_data_of(const ModelVolume &mv)
Definition GLGizmoTextureDisplacement.cpp:5692
std::string m_painted_area_key
Definition GLGizmoTextureDisplacement.hpp:877
static bool collect_paint_region(const TriangleMesh &mesh, const TextureDisplacementFacetsData &facets, std::vector< uint8_t > &region, std::array< LayerPaintMap, TEXTURE_DISPLACEMENT_MAX_LAYERS > *paint)
Definition GLGizmoTextureDisplacement.cpp:5443
double area(const ExPolygon &poly)
Definition ExPolygon.hpp:511
Transform3d texture_displacement_volume_to_world(const ModelVolume &volume)
Definition TextureDisplacement.cpp:3595
Transform3d texture_displacement_bake_frame(const Transform3d &volume_to_world, const Vec3d &anchor)
Definition TextureDisplacement.cpp:3581

References Slic3r::area(), collect_paint_region(), Slic3r::cross(), Slic3r::TriangleMesh::facets_count(), facets_data_of(), Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, Slic3r::TriangleMesh::its, m_painted_area_key, m_painted_area_mm2, m_preview_generation, Slic3r::ModelVolume::mesh(), Slic3r::REFINE_PAINTED, Slic3r::texture_displacement_bake_frame(), and Slic3r::texture_displacement_volume_to_world().

Referenced by estimated_refined_triangles().

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

◆ painted_colors()

GLGizmoTextureDisplacement::PaintedColors Slic3r::GUI::GLGizmoTextureDisplacement::painted_colors ( const TriangleMesh & mesh,
const TriangleSelector::TriangleSplittingData & paint )
static
1866{
1867 PaintedColors out;
1868 TriangleSelector selector(mesh);
1869 selector.deserialize(paint, false);
1871 if (state == EnforcerBlockerType::NONE)
1872 continue;
1873 std::vector<int> source;
1874 const indexed_triangle_set part = selector.get_facets_strict(state, &source);
1875 // Every state comes back over the same vertex array, only the triangles differ.
1876 if (out.facets.vertices.empty())
1877 out.facets.vertices = part.vertices;
1878 out.facets.indices.insert(out.facets.indices.end(), part.indices.begin(), part.indices.end());
1879 out.source.insert(out.source.end(), source.begin(), source.end());
1880 out.state.resize(out.facets.indices.size(), int(state));
1881 }
1882 return out;
1883}
static std::vector< EnforcerBlockerType > extract_used_facet_states(const TriangleSplittingData &data)
Definition TriangleSelector.cpp:2340

References Slic3r::TriangleSelector::deserialize(), Slic3r::TriangleSelector::extract_used_facet_states(), Slic3r::GUI::GLGizmoTextureDisplacement::PaintedColors::facets, Slic3r::TriangleSelector::get_facets_strict(), Slic3r::NONE, Slic3r::GUI::GLGizmoTextureDisplacement::PaintedColors::source, and Slic3r::GUI::GLGizmoTextureDisplacement::PaintedColors::state.

Referenced by rebuild_painted_colors().

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

◆ painted_parts()

std::vector< ModelVolume * > Slic3r::GUI::GLGizmoTextureDisplacement::painted_parts ( )
private
3816{
3817 std::vector<ModelVolume *> out;
3818 for (ModelVolume *mv : texture_parts())
3819 if (mv->is_texture_displacement_painted())
3820 out.push_back(mv);
3821 return out;
3822}

References texture_parts().

Referenced by bake(), queue_prepare(), and queue_preview_job().

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

◆ palette_filaments()

std::vector< int > Slic3r::GUI::GLGizmoTextureDisplacement::palette_filaments ( const std::vector< PaletteEntry > & palette,
const std::vector< uint8_t > & triangle_color,
const std::function< int(const PaletteEntry &)> & slot_for_mix )
static
5998{
5999 std::vector<char> used(palette.size(), 0);
6000 for (const uint8_t v : triangle_color)
6001 if (v > 0 && size_t(v) <= palette.size())
6002 used[size_t(v) - 1] = 1;
6003
6004 std::vector<int> out(palette.size(), -1);
6005 for (size_t i = 0; i < palette.size(); ++i) {
6006 const PaletteEntry &e = palette[i];
6007 if (!e.is_mix()) {
6008 out[i] = e.a;
6009 } else if (used[i]) {
6010 const int slot = slot_for_mix ? slot_for_mix(e) : -1;
6011 // No room for another slot: the component that dominates the blend is the nearest the print
6012 // can come.
6013 out[i] = slot >= 0 ? slot : (e.num * 2 >= e.den ? e.a : e.b);
6014 }
6015 }
6016 return out;
6017}

References Slic3r::PrintableColor::a, Slic3r::PrintableColor::b, Slic3r::PrintableColor::den, Slic3r::PrintableColor::is_mix(), and Slic3r::PrintableColor::num.

Referenced by Slic3r::GUI::TextureDisplacementBakeJob::finalize().

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

◆ palette_quantizers()

std::pair< ColorQuantizeFn, ColorQuantizeFn > Slic3r::GUI::GLGizmoTextureDisplacement::palette_quantizers ( )
5780{
5782 if (!m_palette_quantizer) {
5784 const bool has_mixes = m_palette_cache.size() > m_palette_filaments.size();
5786 }
5788}
static ColorQuantizeFn make_palette_quantizer(const std::vector< PaletteEntry > &palette)
Definition GLGizmoTextureDisplacement.cpp:6019

References cached_palette(), m_palette_cache, m_palette_filaments, m_palette_pure_quantizer, m_palette_quantizer, make_palette(), and make_palette_quantizer().

Referenced by rebuild_subdivide_preview().

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

◆ panel_tooltip() [1/2]

void Slic3r::GUI::GLGizmoTextureDisplacement::panel_tooltip ( const std::string & text,
const std::vector< TipMedia > & media = {} )
private
4086{
4087 // Nothing explains a control while the control is being used: dragging a slider, typing a value
4088 // into one, picking from a combo, dragging a layer card. The pointer sits on the very thing being
4089 // edited, so without this the tooltip covers the model exactly while the user is watching it change.
4090 // The full wait comes back afterwards rather than the short one - having just let go of a slider is
4091 // not a reason to be shown a picture of it.
4092 if (ImGui::IsAnyItemActive() || ImGui::IsAnyMouseDown()) {
4093 m_tip_item = ImVec2(-1.f, -1.f);
4094 m_tip_last_shown = -1e9;
4095 return;
4096 }
4097
4098 // The item's position stands in for its identity: moving to another control restarts the wait, and
4099 // staying on this one carries it on.
4100 const double now = tip_now();
4101 const ImVec2 item = ImGui::GetItemRectMin();
4102 if (item.x != m_tip_item.x || item.y != m_tip_item.y) {
4103 m_tip_item = item;
4104 m_tip_hover_since = now;
4105 }
4107 // A pointer resting on a control produces no events at all, so unless the wait asks for frames
4108 // itself nothing redraws the canvas and the wait never finishes - the tooltip then appears only
4109 // on the next pixel of mouse movement.
4110 //
4111 // request_extra_frame() and not schedule_extra_frame(): the latter only arranges a timer, whose
4112 // event an idle event is assumed to follow, and GLCanvas3D::on_idle() renders nothing unless the
4113 // canvas was already dirty when it ran. request_extra_frame() ends on_idle with
4114 // evt.RequestMore(), which obliges wx to deliver the next one. It is what 3DBed's fade uses.
4115 // Costing a few hundred milliseconds of frames is the cheaper half of that trade.
4116 m_parent.request_extra_frame();
4117 return;
4118 }
4119 m_tip_last_shown = now;
4120
4121 // ImGui hides a window on the first frame it is submitted, to measure it before showing it - and
4122 // for a tooltip it does so every time one reappears, because the windows are recycled (see the
4123 // Popup|Tooltip branch of Begin()). So the frame on which a tooltip is decided is never the frame
4124 // on which it is seen, and one that then asks for nothing more stays invisible until the next pixel
4125 // of mouse movement happens to produce another frame. Keep frames coming until it has settled.
4126 const int shown_frame = ImGui::GetFrameCount();
4127 m_tip_settled = (shown_frame == m_tip_shown_frame + 1) ? m_tip_settled + 1 : 0;
4128 m_tip_shown_frame = shown_frame;
4129 if (m_tip_settled < 2)
4130 m_parent.request_extra_frame();
4131
4132 const ImGuiStyle &style = ImGui::GetStyle();
4133 const float gap = m_imgui->scaled(0.5f);
4134 const float wrap = m_imgui->scaled(20.f);
4135 // The width has to be known here, not left to ImGui, because the tooltip is placed by its left edge
4136 // (below) and an illustration has to be laid out against something. One carrying illustrations takes
4137 // the full wrap width; a plain one takes exactly what its text needs, as it did when ImGui sized it,
4138 // so a two-word tooltip is not a wide box with two words in it.
4139 const float text_w = ImGui::CalcTextSize(text.c_str(), nullptr, false, wrap).x;
4140 const float width = (media.empty() ? std::min(wrap, std::ceil(text_w)) : wrap) + 2.f * style.WindowPadding.x;
4141
4142 // Beside the anchor (the panel, or the texture picker when the tooltip comes from inside it), never
4143 // over it - the same reasoning, and the same placement, as the picker popup itself. ImGui normally
4144 // keeps a tooltip glued to the mouse; it honours a position set here instead (see the Orca branch in
4145 // imgui's Begin()), and still clamps the result inside the canvas.
4146 const bool left = m_tooltip_anchor_min.x - gap - width >= 0.f;
4147 const float x = left ? m_tooltip_anchor_min.x - gap - width : m_tooltip_anchor_max.x + gap;
4148 ImGui::SetNextWindowPos(ImVec2(x, ImGui::GetItemRectMin().y)); // level with the control it explains
4149 ImGui::SetNextWindowSize(ImVec2(width, 0.f)); // height still follows the content
4150
4151 ImGui::BeginTooltip();
4152 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));
4153 ImGui::PushTextWrapPos(0.f); // the window's own edge, which is what the fixed width above sets
4154 ImGui::TextUnformatted(text.c_str());
4155 ImGui::PopTextWrapPos();
4156 ImGui::PopStyleColor();
4157 render_tip_media(media, width - 2.f * style.WindowPadding.x);
4158 ImGui::EndTooltip();
4159}
double m_tip_hover_since
Definition GLGizmoTextureDisplacement.hpp:1286
void render_tip_media(const std::vector< TipMedia > &media, float width)
Definition GLGizmoTextureDisplacement.cpp:4161
ImVec2 m_tip_item
Definition GLGizmoTextureDisplacement.hpp:1285
int m_tip_settled
Definition GLGizmoTextureDisplacement.hpp:1291
double m_tip_last_shown
Definition GLGizmoTextureDisplacement.hpp:1287
int m_tip_shown_frame
Definition GLGizmoTextureDisplacement.hpp:1290
static constexpr double TIP_DELAY_AGAIN
Definition GLGizmoTextureDisplacement.cpp:4077
static double tip_now()
Definition GLGizmoTextureDisplacement.cpp:4064
static constexpr double TIP_DELAY
Definition GLGizmoTextureDisplacement.cpp:4076
static constexpr double TIP_STICKY
Definition GLGizmoTextureDisplacement.cpp:4078
coord_t width(const BoundingBox &box)
Definition Arrange.cpp:1184
@ left
Definition MarchingSquares.hpp:188

References Slic3r::GUI::GLGizmoBase::m_imgui, Slic3r::GUI::GLGizmoBase::m_parent, m_tip_hover_since, m_tip_item, m_tip_last_shown, m_tip_settled, m_tip_shown_frame, m_tooltip_anchor_max, m_tooltip_anchor_min, render_tip_media(), style, Slic3r::GUI::TIP_DELAY, Slic3r::GUI::TIP_DELAY_AGAIN, Slic3r::GUI::tip_now(), and Slic3r::GUI::TIP_STICKY.

Referenced by on_render_input_window(), panel_tooltip(), render_debug_stage_panel(), and render_texture_library_popup().

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

◆ panel_tooltip() [2/2]

void Slic3r::GUI::GLGizmoTextureDisplacement::panel_tooltip ( const wxString & text,
const std::vector< TipMedia > & media = {} )
private
4081{
4082 panel_tooltip(into_u8(text), media);
4083}

References Slic3r::GUI::into_u8(), and panel_tooltip().

Here is the call graph for this function:

◆ patch_in_world()

indexed_triangle_set Slic3r::GUI::GLGizmoTextureDisplacement::patch_in_world ( const indexed_triangle_set & patch) const
private
1322{
1323 const ModelVolume *mv = texture_volume();
1324 if (mv == nullptr)
1325 return patch;
1326 // The bake's own frame, so the projection this feeds agrees with what bakes.
1328 if (to_world.matrix().isApprox(Transform3d::Identity().matrix()))
1329 return patch;
1330
1331 indexed_triangle_set world = patch;
1332 for (Vec3f &v : world.vertices)
1333 v = (to_world * v.cast<double>()).cast<float>();
1334 return world;
1335}

References Slic3r::cast(), Slic3r::texture_displacement_bake_frame(), Slic3r::texture_displacement_volume_to_world(), and texture_volume().

Referenced by compute_layer_corner_uvs(), compute_layer_vertex_uvs(), layer_projection_frame(), and update_uv_editor().

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

◆ pick_mixes()

std::vector< GLGizmoTextureDisplacement::PaletteEntry > Slic3r::GUI::GLGizmoTextureDisplacement::pick_mixes ( const std::vector< PaletteEntry > & ranking,
int count,
int free_slots,
const std::function< bool(const PaletteEntry &)> & reusable )
static
5968{
5969 std::vector<PaletteEntry> out;
5970 for (const PaletteEntry &e : ranking) {
5971 if (int(out.size()) >= count)
5972 break;
5973 // Out of free slots, a later mix that already has one still fits.
5974 if (reusable && reusable(e)) {
5975 out.push_back(e);
5976 } else if (free_slots > 0) {
5977 out.push_back(e);
5978 --free_slots;
5979 }
5980 }
5981 return out;
5982}

References Slic3r::GUI::GLGizmoBase::count.

Referenced by cached_palette().

Here is the caller graph for this function:

◆ plan_remesh()

bool Slic3r::GUI::GLGizmoTextureDisplacement::plan_remesh ( const indexed_triangle_set & src,
float target_edge_mm,
float sharp_angle_deg,
indexed_triangle_set & out )
staticprivate
6396{
6397 if (target_edge_mm <= 0.f)
6398 return false;
6399
6400 // Its cost and memory grow with the *square* of 1/target_edge, and it runs single-threaded on the
6401 // UI thread, so the target has to be bounded against the part's actual surface area rather than
6402 // taken at face value. Standard mode asks for 1 mm whatever it is handed, and the slider goes down
6403 // to 0.1 mm: on a 150 mm part those are ~500 k and ~50 M triangles respectively - minutes to hours
6404 // of frozen UI, which is indistinguishable from a hang. Raising the target instead still gives the
6405 // subdivider the even density it needs, and the subdivision that follows is where the detail was
6406 // always going to come from anyway. An equilateral triangle of edge L covers sqrt(3)/4 * L^2.
6407 static constexpr double REMESH_MAX_TRIANGLES = 150000.0;
6408 double area = 0.0;
6409 for (const stl_triangle_vertex_indices &tri : src.indices)
6410 area += 0.5 * double((src.vertices[size_t(tri[1])] - src.vertices[size_t(tri[0])])
6411 .cross(src.vertices[size_t(tri[2])] - src.vertices[size_t(tri[0])])
6412 .norm());
6413 if (const double budget_edge = std::sqrt(area / (0.4330127 * REMESH_MAX_TRIANGLES));
6414 budget_edge > double(target_edge_mm)) {
6415 BOOST_LOG_TRIVIAL(info) << "Texture displacement: remesh target raised from " << target_edge_mm
6416 << " mm to " << budget_edge << " mm to stay within "
6417 << int(REMESH_MAX_TRIANGLES) << " triangles.";
6418 target_edge_mm = float(budget_edge);
6419 }
6420
6421 // Five iterations with three relaxation passes each, rather than CGAL's three-and-one. Splitting
6422 // and collapsing bring edge lengths near the target but leave the vertices where they fell, so it
6423 // is the relaxation count that decides how even the result looks - and three passes in total was
6424 // nowhere near enough. Fifteen costs proportionally more, but only on an explicit Remesh.
6425 indexed_triangle_set remeshed = MeshBoolean::cgal::remesh_isotropic(
6426 src, double(target_edge_mm), /* iterations */ 5, double(sharp_angle_deg),
6427 /* relaxation steps */ 3);
6428 // remesh_isotropic() signals failure by handing the input straight back, so compare against it
6429 // structurally. Vertex count alone is not enough: a remesh that only redistributes triangles at
6430 // roughly the current density legitimately lands on the same count, and treating that as failure
6431 // meant a perfectly good result got thrown away with an error message.
6432 if (remeshed.indices.empty() ||
6433 (remeshed.vertices.size() == src.vertices.size() && remeshed.indices.size() == src.indices.size() &&
6434 remeshed.indices == src.indices))
6435 return false;
6436
6437 out = std::move(remeshed);
6438 return true;
6439}
indexed_triangle_set remesh_isotropic(const indexed_triangle_set &mesh, double target_edge_length, unsigned n_iterations, double sharp_angle_deg, unsigned n_relaxation_steps)
Definition MeshBoolean.cpp:291

References Slic3r::area(), and Slic3r::MeshBoolean::cgal::remesh_isotropic().

Referenced by prepare_mesh().

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

◆ prepare_mesh()

TextureDisplacementPrepareResult Slic3r::GUI::GLGizmoTextureDisplacement::prepare_mesh ( const indexed_triangle_set & base,
const TextureDisplacementFacetsData & masks,
const std::vector< TextureDisplacementLayer > & layers,
const std::vector< Vec2f > & model_uv,
const Transform3d & texture_frame,
const TextureDisplacementPrepareParams & params,
const TextureColorSettings & color_settings,
const DisplacementProgressFn & progress,
BakeStageRecorder * debug = nullptr )
static
6077{
6078 TextureDisplacementPrepareResult out;
6079 const auto report = [&progress](int pct) { return !progress || progress(pct); };
6080
6081 // The samplers are built over a copy of the current mesh moved into `texture_frame`, and queried
6082 // with points and normals mapped across, so the stages below go on measuring the mesh itself.
6083 const bool framed = !texture_frame.matrix().isApprox(Transform3d::Identity().matrix());
6084 const Matrix3d frame_normal = texture_frame.linear().inverse().transpose();
6085 indexed_triangle_set framed_mesh;
6086 const auto mesh_to_sample = [&](const indexed_triangle_set &its) -> const indexed_triangle_set & {
6087 if (!framed)
6088 return its;
6089 framed_mesh = its;
6090 for (Vec3f &v : framed_mesh.vertices)
6091 v = (texture_frame * v.cast<double>()).cast<float>();
6092 return framed_mesh;
6093 };
6094 const auto sample_in_frame = [&](auto sampler) -> decltype(sampler) {
6095 if (!framed || !sampler)
6096 return sampler;
6097 return [sampler, frame = texture_frame, frame_normal](const Vec3f &pos, const Vec3f &normal) {
6098 return sampler((frame * pos.cast<double>()).cast<float>(),
6099 (frame_normal * normal.cast<double>()).normalized().cast<float>());
6100 };
6101 };
6102
6103 // Stage capture for the debug view. Timed from the end of the previous stage, so the capture -
6104 // a copy plus an edge scan - never lands inside the measurement it reports.
6105 auto stage_clock = std::chrono::steady_clock::now();
6106 const auto capture = [&](const char *name, const indexed_triangle_set &m,
6107 const std::string &detail = {}) {
6108 if (debug == nullptr)
6109 return;
6110 const double ms =
6111 std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - stage_clock).count();
6112 debug->capture(name, m.vertices, m.indices, ms, detail);
6113 stage_clock = std::chrono::steady_clock::now();
6114 };
6115 capture("input", base, "as handed to prepare");
6116
6117 TriangleMesh mesh(base);
6118 TextureDisplacementFacetsData current = masks;
6119 bool changed = false;
6120 bool had_paint = false;
6121 for (const TriangleSelector::TriangleSplittingData &m : masks)
6123 // The model's own UV channel for whatever `mesh` currently is. Every stage below keeps the input's
6124 // surface - it evens out, refines or cuts the triangles lying on it - so each corner reads its uv
6125 // straight off `base`'s channel at its own position instead of being carried stage by stage, which
6126 // would compound each stage's approximation. Untouched while nothing has replaced the mesh yet.
6127 const auto uv_now = [&]() {
6128 return (model_uv.empty() || !changed) ? model_uv : transfer_uv_channel(base, model_uv, mesh.its);
6129 };
6130 if (!report(1))
6131 return out;
6132
6133 // 1. Even out the triangle density, and carry the paint onto the result. Everything downstream is
6134 // driven by that paint, so losing it is a hard stop rather than something to bake around - and
6135 // stopping here, before anything is committed, leaves the user's model exactly as it was.
6136 if (params.remesh_edge_mm > 0.f) {
6137 indexed_triangle_set remeshed;
6138 if (plan_remesh(mesh.its, params.remesh_edge_mm, params.remesh_sharp_deg, remeshed)) {
6139 TriangleMesh new_mesh(std::move(remeshed));
6140 const AABBTreeIndirect::Tree3f old_tree =
6141 AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices);
6143 bool any = false;
6144 for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i) {
6145 // Both meshes are the volume's own local frame, so there is no shift between them.
6146 carried[size_t(i)] =
6147 remap_texture_paint_spatial(mesh, current[size_t(i)], old_tree, new_mesh, Vec3f::Zero());
6148 any = any || !carried[size_t(i)].bitstream.empty();
6149 }
6150 mesh = std::move(new_mesh);
6151 current = std::move(carried);
6152 changed = true;
6153 capture("remesh", mesh.its,
6154 "target " + std::to_string(params.remesh_edge_mm) + " mm" +
6155 (had_paint && !any ? ", paint lost" : ""));
6156 if (had_paint && !any) {
6157 out.paint_lost = true;
6158 return out;
6159 }
6160 } else if (debug != nullptr) {
6161 capture("remesh", mesh.its, "skipped: remeshing changed nothing");
6162 }
6163 }
6164 if (!report(50))
6165 return {};
6166
6167 // 2. Refine where the texture bends - the painted area only, plus the graded band the conformal
6168 // closure pulls in around it.
6169 if (params.subdiv_target_mm > 0.f) {
6170 std::vector<uint8_t> region;
6171 std::array<LayerPaintMap, TEXTURE_DISPLACEMENT_MAX_LAYERS> paint;
6172 if (collect_paint_region(mesh, current, region, &paint)) {
6173 // Feature-adaptive: sample the combined displacement so refinement follows texture
6174 // curvature. A null sampler (only LSCM layers, or nothing decodable) falls back to the
6175 // length baseline alone.
6176 const std::vector<Vec2f> uv_stage = uv_now();
6177 const std::vector<Vec2f> *uv_ptr = uv_stage.empty() ? nullptr : &uv_stage;
6178 const indexed_triangle_set &sampled = mesh_to_sample(mesh.its);
6179 HeightFieldSampler sampler;
6180 if (params.subdiv_feature)
6181 sampler = sample_in_frame(make_combined_displacement_sampler(sampled, layers, current, uv_ptr));
6182 // Colour boundaries need triangles of their own - the chord test cannot see them, since
6183 // the height field is perfectly smooth across a change of filament.
6184 ColorFieldSampler color;
6185 // A flat-colour layer is matched against single filaments only, as the bake does, so its
6186 // boundaries are refined where the bake will actually change filament.
6187 if (params.subdiv_color_edge_mm > 0.f && !color_settings.empty())
6188 color = sample_in_frame(make_combined_color_sampler(sampled, layers, current,
6189 make_palette_quantizer(color_settings.palette),
6190 make_palette_quantizer(color_settings.palette_pure),
6191 uv_ptr));
6192 // The refinement follows perceived colour: a mix is one colour here, however the slicer
6193 // interleaves its filaments layer by layer.
6194 // "Min edge" is a feature-mode control (it is the floor the curvature test refines down
6195 // to); in plain adaptive mode the target edge length is the only criterion, so the floor
6196 // must not be allowed to silently override a target the user set below it.
6197 const float tol = params.subdiv_feature ? params.subdiv_detail_mm : 0.f;
6198 const float floor = params.subdiv_feature ? params.subdiv_min_edge_mm : 0.f;
6199 // Step textures (a grid, a knurl: two levels with sharp edges between them) are cut into
6200 // walls after refining rather than refined into ramps, see 3. below. The verdict is taken
6201 // here, on the coarse mesh, because the refinement has to know: in step mode it leaves
6202 // the stepped triangles to the cutter, which is only right if the cutter then cuts them.
6203 // The step width and the seam gap both span two texels of the finest layer: the bilinear
6204 // blend across a step is a texel wide, so a seam copy a texel from the step samples a
6205 // pure side.
6206 float texel_mm = std::numeric_limits<float>::max();
6207 if (params.cut_steps && sampler)
6208 for (const TextureDisplacementLayer &layer : layers) {
6209 if (layer.empty())
6210 continue;
6211 const DecodedHeightTexture &tex = decode_height_texture(layer);
6212 if (tex.width > 0 && layer.tiling_scale > 0.f)
6213 texel_mm = std::min(texel_mm, layer.tiling_scale / float(tex.width));
6214 }
6215 const bool cut_steps = texel_mm < std::numeric_limits<float>::max() &&
6216 texture_has_steps_to_cut(mesh.its, region, sampler, 2.f * texel_mm, 2.f * texel_mm);
6217 bool aborted = false;
6218 std::vector<int> source;
6219 // The budget is "triangles the refinement may *add*", so the mesh's own count is the
6220 // baseline - otherwise the control would be meaningless (or a dead end) on a dense model.
6221 indexed_triangle_set refined =
6222 subdivide_mesh_adaptive(mesh.its, region, params.subdiv_target_mm,
6223 int(mesh.its.indices.size()) + params.subdiv_added_triangles, &source,
6224 sampler, tol, floor, params.subdiv_border_mm, [&](int pct) {
6225 aborted = !report(50 + pct / 2);
6226 return !aborted;
6227 },
6228 color, params.subdiv_color_edge_mm, cut_steps);
6229 if (aborted)
6230 return {};
6231 if (refined.indices.size() != mesh.its.indices.size()) {
6232 mesh = TriangleMesh(std::move(refined));
6233 current = masks_after_subdivision(mesh, source, paint);
6234 changed = true;
6235 capture("adaptive subdivide", mesh.its,
6236 params.subdiv_feature ? "feature adaptive" : "length only");
6237 } else if (debug != nullptr) {
6238 capture("adaptive subdivide", mesh.its, "skipped: already meets the criteria");
6239 }
6240
6241 // 3. Cut the painted area along the texture's sharp steps so they bake as walls. The
6242 // refinement above left the stepped triangles alone, so this is where a binary
6243 // texture's edges get their geometry. Paint is carried the same way as through the
6244 // subdivision: every output triangle lies inside the pre-subdivision triangle it
6245 // descends from, so the source maps compose.
6246 if (cut_steps) {
6247 std::vector<uint8_t> cut_region(mesh.its.indices.size(), 0);
6248 for (size_t i = 0; i < cut_region.size() && i < source.size(); ++i)
6249 cut_region[i] = (region[size_t(source[i])] & REFINE_PAINTED) ? 1 : 0;
6250 const std::vector<Vec2f> uv_refined = uv_now();
6251 const HeightFieldSampler refined_sampler =
6252 sample_in_frame(make_combined_displacement_sampler(mesh_to_sample(mesh.its), layers, current,
6253 uv_refined.empty() ? nullptr : &uv_refined));
6254 std::vector<int> cut_source;
6255 size_t cuts = 0;
6256 indexed_triangle_set cut = cut_mesh_at_steps(mesh.its, cut_region, refined_sampler,
6257 2.f * texel_mm, 2.f * texel_mm, 0.f,
6258 &cut_source, &cuts);
6259 if (cuts > 0) {
6260 for (int &s : cut_source)
6261 s = source[size_t(s)];
6262 mesh = TriangleMesh(std::move(cut));
6263 current = masks_after_subdivision(mesh, cut_source, paint);
6264 changed = true;
6265 capture("step cut", mesh.its, std::to_string(cuts) + " triangles cut");
6266 } else if (debug != nullptr) {
6267 capture("step cut", mesh.its, "skipped: the refined mesh has no steps left to cut");
6268 }
6269 } else if (debug != nullptr && params.cut_steps) {
6270 capture("step cut", mesh.its, "skipped: not a step texture");
6271 }
6272 }
6273 }
6274 if (!report(100) || !changed)
6275 return out; // an empty result: nothing to commit, which is not a failure
6276
6277 out.uv = uv_now();
6278 out.mesh = std::move(mesh.its);
6279 out.masks = std::move(current);
6280 return out;
6281}
static bool plan_remesh(const indexed_triangle_set &src, float target_edge_mm, float sharp_angle_deg, indexed_triangle_set &out)
Definition GLGizmoTextureDisplacement.cpp:6394
static TextureDisplacementFacetsData masks_after_subdivision(const TriangleMesh &new_mesh, const std::vector< int > &source, const std::array< LayerPaintMap, TEXTURE_DISPLACEMENT_MAX_LAYERS > &paint)
Definition GLGizmoTextureDisplacement.cpp:5600
Tree< 3, typename VertexType::Scalar > build_aabb_tree_over_indexed_triangle_set(const std::vector< VertexType > &vertices, const std::vector< IndexedFaceType > &faces, const typename VertexType::Scalar eps=0)
Definition AABBTreeIndirect.hpp:677
Tree< 3, float > Tree3f
Definition AABBTreeIndirect.hpp:222
TriangleSelector::TriangleSplittingData remap_texture_paint_spatial(const TriangleMesh &src_mesh, const TriangleSelector::TriangleSplittingData &src_data, const AABBTreeIndirect::Tree3f &src_tree, const TriangleMesh &dst_mesh, const Vec3f &dst_to_src)
Definition GLGizmoTextureDisplacement.cpp:222
std::function< float(const Vec3f &pos, const Vec3f &normal)> HeightFieldSampler
Definition TextureDisplacement.hpp:1104
std::function< int(const Vec3f &pos, const Vec3f &normal)> ColorFieldSampler
Definition TextureDisplacement.hpp:1120
bool texture_has_steps_to_cut(const indexed_triangle_set &mesh, const std::vector< uint8_t > &region, const HeightFieldSampler &sampler, float step_width_mm, float seam_gap_mm, float min_feature_mm)
Definition TextureDisplacement.cpp:4961
indexed_triangle_set cut_mesh_at_steps(const indexed_triangle_set &mesh, const std::vector< uint8_t > &region, const HeightFieldSampler &sampler, float step_width_mm, float seam_gap_mm, float min_feature_mm, std::vector< int > *out_source, size_t *out_cut_count)
Definition TextureDisplacement.cpp:4968
indexed_triangle_set subdivide_mesh_adaptive(const indexed_triangle_set &mesh, const std::vector< uint8_t > &refine_region, float target_edge_length_mm, int max_triangles, std::vector< int > *out_source, const HeightFieldSampler &sampler, float chord_tolerance_mm, float min_edge_length_mm, float border_edge_length_mm, const DisplacementProgressFn &progress, const ColorFieldSampler &color, float color_edge_length_mm, bool split_multi_crossings)
Definition TextureDisplacement.cpp:3964
ColorFieldSampler make_combined_color_sampler(const indexed_triangle_set &base_mesh, const std::vector< TextureDisplacementLayer > &layers, const TextureDisplacementFacetsData &facets_data, ColorQuantizeFn quantize, ColorQuantizeFn quantize_pure, const std::vector< Vec2f > *model_uv)
Definition TextureDisplacement.cpp:3840
Point normal(Point pt, double scale)
Definition TreeSupport.cpp:112
std::vector< Vec2f > transfer_uv_channel(const indexed_triangle_set &src_mesh, const std::vector< Vec2f > &src_uv, const indexed_triangle_set &dst_mesh, float max_distance)
Definition TextureDisplacement.cpp:2126
HeightFieldSampler make_combined_displacement_sampler(const indexed_triangle_set &base_mesh, const std::vector< TextureDisplacementLayer > &layers, const TextureDisplacementFacetsData &facets_data, const std::vector< Vec2f > *model_uv)
Definition TextureDisplacement.cpp:3878

References Slic3r::AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(), Slic3r::BakeStageRecorder::capture(), Slic3r::cast(), collect_paint_region(), Slic3r::cut_mesh_at_steps(), Slic3r::TextureDisplacementPrepareParams::cut_steps, Slic3r::decode_height_texture(), Slic3r::TextureColorSettings::empty(), Slic3r::ENFORCER, Slic3r::TriangleSelector::has_facets(), Slic3r::TriangleMesh::its, Slic3r::make_combined_color_sampler(), Slic3r::make_combined_displacement_sampler(), make_palette_quantizer(), Slic3r::TextureDisplacementPrepareResult::masks, masks_after_subdivision(), Slic3r::TextureDisplacementPrepareResult::mesh, Slic3r::normal(), Slic3r::TextureDisplacementPrepareResult::paint_lost, Slic3r::TextureColorSettings::palette, Slic3r::TextureColorSettings::palette_pure, plan_remesh(), Slic3r::REFINE_PAINTED, Slic3r::TextureDisplacementPrepareParams::remesh_edge_mm, Slic3r::TextureDisplacementPrepareParams::remesh_sharp_deg, Slic3r::TextureDisplacementPrepareParams::subdiv_added_triangles, Slic3r::TextureDisplacementPrepareParams::subdiv_border_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_color_edge_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_detail_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_feature, Slic3r::TextureDisplacementPrepareParams::subdiv_min_edge_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_target_mm, Slic3r::subdivide_mesh_adaptive(), Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS, Slic3r::texture_has_steps_to_cut(), Slic3r::transfer_uv_channel(), Slic3r::TextureDisplacementPrepareResult::uv, and Slic3r::DecodedHeightTexture::width.

Referenced by Slic3r::GUI::TextureDisplacementDebugJob::process(), and Slic3r::GUI::TextureDisplacementPrepareJob::process().

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

◆ prepare_texture_frame()

Transform3d Slic3r::GUI::GLGizmoTextureDisplacement::prepare_texture_frame ( const ModelVolume & part)
private
3873{
3874 if (texture_parts().size() < 2)
3875 return Transform3d::Identity();
3877}

References texture_anchor(), Slic3r::texture_displacement_bake_frame(), Slic3r::texture_displacement_volume_to_world(), and texture_parts().

Referenced by queue_prepare(), and run_stage_debug().

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

◆ preview_part()

const ModelVolume * Slic3r::GUI::GLGizmoTextureDisplacement::preview_part ( const PartPreview & preview) const
1303{
1304 const ModelObject *mo = m_c->selection_info()->model_object();
1305 if (mo == nullptr)
1306 return nullptr;
1307 for (const ModelVolume *mv : mo->volumes)
1308 if (mv->id() == preview.volume_id)
1309 return mv;
1310 return nullptr; // the part was removed, or its mesh replaced, since the preview was built
1311}

References Slic3r::ObjectBase::id(), Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoTextureDisplacement::PartPreview::volume_id, and Slic3r::ModelObject::volumes.

Referenced by render_painter_gizmo(), and render_preview_mesh().

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

◆ pro_mode()

bool Slic3r::GUI::GLGizmoTextureDisplacement::pro_mode ( ) const
inlineprivate
361{ return m_panel_mode != 0; }

References m_panel_mode.

Referenced by on_render_input_window(), and run_stage_debug().

Here is the caller graph for this function:

◆ process_uv_commands()

void Slic3r::GUI::GLGizmoTextureDisplacement::process_uv_commands ( )
private
2838{
2839 std::vector<std::pair<int, float>> queue;
2840 queue.swap(m_uv_command_queue);
2841 for (const auto &[cmd, value] : queue)
2842 run_uv_command(cmd, value);
2843}
void run_uv_command(int cmd, float value)
Definition GLGizmoTextureDisplacement.cpp:2971

References m_uv_command_queue, and run_uv_command().

Referenced by on_render_input_window().

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

◆ push_uv_pane_state()

void Slic3r::GUI::GLGizmoTextureDisplacement::push_uv_pane_state ( )
private
2896{
2897 UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas();
2898 if (canvas == nullptr)
2899 return;
2900
2901 UVEditorCanvas::PaneState st;
2902 const ModelVolume *mv = texture_volume();
2903 const TextureDisplacementLayer *layer = active_layer();
2904 if (mv != nullptr && layer != nullptr && layer->projection_method == TextureProjectionMethod::LSCM) {
2905 st.has_layer = true;
2906 st.layer_name = from_u8(layer->name.empty() ? Slic3r::format(_u8L("Layer %1%"), layer->slot + 1) : layer->name);
2907 st.tile_mm = layer->tiling_scale;
2908 st.seam_angle_deg = layer->lscm_seam_angle_deg;
2909 st.connect_islands = layer->auto_connect_islands;
2910 st.has_seams = !layer->lscm_seam_edges.empty();
2911 st.has_uv_edits = !layer->lscm_uv_overrides.empty();
2912
2913 // Retried while empty: the image may not decode yet, and a blank thumbnail must not stick.
2914 if (layer->image_data.get() != m_uv_thumb_source || m_uv_thumb_rgb.empty()) {
2915 m_uv_thumb_source = nullptr;
2916 m_uv_thumb_rgb.clear();
2917 const DecodedHeightTexture tex = decode_height_texture(*layer);
2918 if (!tex.empty()) {
2919 m_uv_thumb_source = layer->image_data.get();
2920 // Box-filtered, so a fine pattern such as bricks averages out instead of aliasing into noise.
2921 const int n = UV_THUMB_PX;
2922 const bool color = tex.has_color();
2923 m_uv_thumb_rgb.resize(size_t(n) * n * 3);
2924 for (int y = 0; y < n; ++y) {
2925 const int y0 = y * tex.height / n, y1 = std::max(y0 + 1, (y + 1) * tex.height / n);
2926 for (int x = 0; x < n; ++x) {
2927 const int x0 = x * tex.width / n, x1 = std::max(x0 + 1, (x + 1) * tex.width / n);
2928 uint64_t sum[3]{};
2929 for (int sy = y0; sy < y1; ++sy)
2930 for (int sx = x0; sx < x1; ++sx) {
2931 const size_t src = size_t(sy) * size_t(tex.width) + size_t(sx);
2932 for (int ch = 0; ch < 3; ++ch)
2933 sum[ch] += color ? tex.rgb[src * 3 + ch] : tex.pixels[src];
2934 }
2935 const uint64_t count = uint64_t(y1 - y0) * uint64_t(x1 - x0);
2936 for (int ch = 0; ch < 3; ++ch)
2937 m_uv_thumb_rgb[size_t(y * n + x) * 3 + ch] = (unsigned char) (sum[ch] / count);
2938 }
2939 }
2940 }
2941 }
2942 if (!m_uv_thumb_rgb.empty()) {
2943 st.thumbnail_px = UV_THUMB_PX;
2944 st.thumbnail_rgb = m_uv_thumb_rgb;
2945 }
2946
2947 if (!m_uv_editor_unwrap.empty()) {
2948 // Stale when the unwrap on screen was made from another layer, other paint, other seams or another
2949 // seam angle than this layer has now - the same things update_uv_editor() re-solves for.
2950 UVEditorState now;
2952 now.image_data = layer->image_data.get();
2953 now.seam_angle = layer->lscm_seam_angle_deg;
2954 now.padding = layer->island_padding_mm;
2955 now.facets = mv->texture_displacement_facet(m_active_layer_slot).get_data();
2956 now.seam_edges = layer->lscm_seam_edges;
2957 st.unwrapped = true;
2958 st.unwrap_stale = !(now == m_uv_editor_state);
2959 st.island_count = m_uv_editor_unwrap.chart_count;
2960 st.face_count = m_uv_editor_unwrap.indices.size();
2961 }
2962 }
2963 st.mark_seams = m_seam_edit_mode;
2964 st.seam_path = m_seam_path_mode;
2968 canvas->set_pane_state(std::move(st));
2969}
static constexpr int UV_THUMB_PX
Definition GLGizmoTextureDisplacement.hpp:698
std::vector< unsigned char > m_uv_thumb_rgb
Definition GLGizmoTextureDisplacement.hpp:700
const void * m_uv_thumb_source
Definition GLGizmoTextureDisplacement.hpp:699
UVEditorState m_uv_editor_state
Definition GLGizmoTextureDisplacement.hpp:1172
@ Checker
Definition UVEditorCanvas.hpp:123
@ Distortion
Definition UVEditorCanvas.hpp:123
@ Height
Definition UVEditorCanvas.hpp:123
Definition GLGizmoTextureDisplacement.hpp:1156
int slot
Definition GLGizmoTextureDisplacement.hpp:1157

References _u8L, active_layer(), Slic3r::TextureDisplacementLayer::auto_connect_islands, Slic3r::GUI::UVEditorCanvas::PaneState::background, Checker, Slic3r::GUI::UVEditorCanvas::Checker, Slic3r::GUI::UVEditorCanvas::PaneState::connect_islands, Slic3r::GUI::GLGizmoBase::count, Slic3r::decode_height_texture(), Distortion, Slic3r::GUI::UVEditorCanvas::Distortion, Slic3r::DecodedHeightTexture::empty(), Slic3r::GUI::UVEditorCanvas::PaneState::face_count, Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::facets, Slic3r::format(), Slic3r::GUI::from_u8(), Slic3r::FacetsAnnotation::get_data(), Slic3r::DecodedHeightTexture::has_color(), Slic3r::GUI::UVEditorCanvas::PaneState::has_layer, Slic3r::GUI::UVEditorCanvas::PaneState::has_seams, Slic3r::GUI::UVEditorCanvas::PaneState::has_uv_edits, Slic3r::GUI::UVEditorCanvas::Height, Slic3r::DecodedHeightTexture::height, Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::image_data, Slic3r::TextureDisplacementLayer::image_data, Slic3r::GUI::UVEditorCanvas::PaneState::island_count, Slic3r::TextureDisplacementLayer::island_padding_mm, Slic3r::GUI::UVEditorCanvas::PaneState::layer_name, Slic3r::LSCM, Slic3r::TextureDisplacementLayer::lscm_seam_angle_deg, Slic3r::TextureDisplacementLayer::lscm_seam_edges, Slic3r::TextureDisplacementLayer::lscm_uv_overrides, m_active_layer_slot, m_seam_edit_mode, m_seam_path_mode, m_uv_check_mode, m_uv_editor_state, m_uv_editor_unwrap, m_uv_thumb_rgb, m_uv_thumb_source, Slic3r::GUI::UVEditorCanvas::PaneState::mark_seams, Slic3r::TextureDisplacementLayer::name, Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::padding, Slic3r::DecodedHeightTexture::pixels, Slic3r::TextureDisplacementLayer::projection_method, Slic3r::DecodedHeightTexture::rgb, Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::seam_angle, Slic3r::GUI::UVEditorCanvas::PaneState::seam_angle_deg, Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::seam_edges, Slic3r::GUI::UVEditorCanvas::PaneState::seam_path, Slic3r::GUI::UVEditorCanvas::set_pane_state(), Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::slot, Slic3r::TextureDisplacementLayer::slot, Slic3r::ModelVolume::texture_displacement_facet(), texture_volume(), Slic3r::GUI::UVEditorCanvas::PaneState::thumbnail_px, Slic3r::GUI::UVEditorCanvas::PaneState::thumbnail_rgb, Slic3r::GUI::UVEditorCanvas::PaneState::tile_mm, Slic3r::TextureDisplacementLayer::tiling_scale, Slic3r::GUI::UVEditorCanvas::PaneState::unwrap_stale, Slic3r::GUI::UVEditorCanvas::PaneState::unwrapped, UV_THUMB_PX, and Slic3r::DecodedHeightTexture::width.

Referenced by run_uv_command(), and update_uv_editor().

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

◆ queue_prepare()

void Slic3r::GUI::GLGizmoTextureDisplacement::queue_prepare ( const TextureDisplacementPrepareParams & params,
const std::string & snapshot_name,
bool then_bake,
const std::string & unchanged_msg,
PrepareScope scope = PrepareScope::PaintedParts )
private
6931{
6932 ModelVolume *owner = texture_volume();
6933 if (owner == nullptr || m_prepare_in_progress || m_bake_in_progress)
6934 return;
6935
6936 // Preparing only the layer stack's own part would hand the displacement a coarse mesh everywhere
6937 // else - a height map can only move vertices that already exist, so on a multibody object that is
6938 // the difference between relief and a flat face. See PrepareScope for which parts that means.
6939 const std::vector<ModelVolume *> parts = scope == PrepareScope::AllParts ? texture_parts() : painted_parts();
6940 if (parts.empty())
6941 return;
6942
6943 TextureDisplacementPrepareInput input;
6944 input.params = params;
6945 input.snapshot_name = snapshot_name;
6946 input.volumes.reserve(parts.size());
6947 for (const ModelVolume *part : parts) {
6948 TextureDisplacementPrepareVolume entry;
6949 entry.volume_id = part->id();
6950 entry.base_mesh = part->mesh().its;
6951 entry.masks = facets_data_of(*part);
6952 entry.layers = layers_for_part(*part);
6953 if (part->has_texture_uv())
6954 entry.model_uv = part->texture_uv;
6955 entry.texture_frame = prepare_texture_frame(*part);
6956 input.volumes.push_back(std::move(entry));
6957 }
6958 if (params.subdiv_color_edge_mm > 0.f)
6959 input.color = color_settings_for(*owner);
6960
6961 m_prepare_in_progress = true;
6962 queue_texture_displacement_prepare(std::move(input), [this, then_bake, unchanged_msg](
6964 m_prepare_in_progress = false;
6965 // The commit replaced the volume's mesh (new id, new topology) without changing the object's id
6966 // or volume count, which is not something GLGizmoPainterBase::data_changed() can detect - so the
6967 // reload is explicit, exactly as it is after a bake. Done for every outcome: even a run that
6968 // committed nothing may have left the panel showing a stale triangle count.
6969 if (m_state == On && m_c->selection_info() && m_c->selection_info()->model_object())
6971 m_parent.set_as_dirty();
6972
6973 switch (outcome) {
6975 return; // cancelled, or the volume went away while the job ran - say nothing, do nothing
6977 show_error(nullptr, _u8L("The painted area could not be carried onto the remeshed model, so "
6978 "nothing was changed. Switch to Pro mode and remesh before painting."));
6979 return;
6981 if (!unchanged_msg.empty())
6982 show_error(nullptr, unchanged_msg);
6983 break;
6985 break;
6986 }
6987 // ... and then the displacement itself, in the background exactly as the Pro-mode button does.
6988 // It commits into the snapshot the prepare opened - but only if the prepare opened one: a run
6989 // that found nothing to do took none, and a bake chained onto that has to push its own or it
6990 // would not be undoable at all.
6991 if (then_bake)
6992 bake(/* own_snapshot */ outcome == TextureDisplacementPrepareOutcome::Unchanged);
6993 });
6994}
@ AllParts
Definition GLGizmoTextureDisplacement.hpp:389
Transform3d prepare_texture_frame(const ModelVolume &part)
Definition GLGizmoTextureDisplacement.cpp:3872
void queue_texture_displacement_prepare(TextureDisplacementPrepareInput &&input, std::function< void(TextureDisplacementPrepareOutcome)> on_finished)
Definition TextureDisplacementPrepareJob.cpp:149
TextureDisplacementPrepareOutcome
Definition TextureDisplacementPrepareJob.hpp:33
@ Unchanged
Definition TextureDisplacementPrepareJob.hpp:35
@ PaintLost
Definition TextureDisplacementPrepareJob.hpp:36
@ Failed
Definition TextureDisplacementPrepareJob.hpp:37
@ Committed
Definition TextureDisplacementPrepareJob.hpp:34

References _u8L, AllParts, bake(), Slic3r::GUI::TextureDisplacementPrepareVolume::base_mesh, Slic3r::GUI::TextureDisplacementPrepareInput::color, color_settings_for(), Slic3r::GUI::Committed, facets_data_of(), Slic3r::GUI::Failed, Slic3r::ObjectID::id, Slic3r::GUI::TextureDisplacementPrepareVolume::layers, layers_for_part(), m_bake_in_progress, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, m_prepare_in_progress, Slic3r::GUI::GLGizmoBase::m_state, Slic3r::GUI::TextureDisplacementPrepareVolume::masks, Slic3r::GUI::TextureDisplacementPrepareVolume::model_uv, Slic3r::GUI::GLGizmoBase::On, painted_parts(), Slic3r::GUI::PaintLost, Slic3r::GUI::TextureDisplacementPrepareInput::params, prepare_texture_frame(), Slic3r::GUI::queue_texture_displacement_prepare(), Slic3r::GUI::show_error(), Slic3r::GUI::TextureDisplacementPrepareInput::snapshot_name, Slic3r::TextureDisplacementPrepareParams::subdiv_color_edge_mm, Slic3r::GUI::TextureDisplacementPrepareVolume::texture_frame, texture_parts(), texture_volume(), Slic3r::GUI::Unchanged, update_from_model_object(), Slic3r::GUI::TextureDisplacementPrepareVolume::volume_id, and Slic3r::GUI::TextureDisplacementPrepareInput::volumes.

Referenced by bake_standard(), remesh_model(), and subdivide_model_adaptive().

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

◆ queue_preview_job()

void Slic3r::GUI::GLGizmoTextureDisplacement::queue_preview_job ( )
private
2455{
2456 // A job in flight when the gizmo closes still runs its completion handler, which would otherwise
2457 // happily queue the follow-up run it was holding - against a gizmo nobody is looking at any more.
2458 if (m_state != On)
2459 return;
2460 const ModelVolume *owner = texture_volume();
2461 if (owner == nullptr)
2462 return;
2463 // Every painted part, so the preview shows the whole of what Bake will do rather than its first
2464 // part - the same set bake() displaces.
2465 const std::vector<ModelVolume *> parts = painted_parts();
2466 if (parts.empty())
2467 return;
2468
2469 // One in flight at a time; everything requested meanwhile collapses into a single follow-up run
2470 // issued from the completion handler. See m_preview_job_running.
2472 m_preview_job_pending = true;
2473 return;
2474 }
2475
2476 const uint64_t generation = m_preview_generation->load();
2477
2478 TextureDisplacementPreviewInput input;
2479 // Captured here rather than read in the handler: the palette is main-thread state, and the preview
2480 // has to be grouped against the same palette it was computed with, not whatever it is by the time
2481 // the result lands. Read off the layer stack's own part, since that is where the stack lives.
2482 input.color = color_settings_for(*owner);
2483 input.volumes.reserve(parts.size());
2484 // Which part each result belongs to, so the handler can tell the layer stack's own preview - the one
2485 // the overlays and the wireframe are keyed to - from the rest.
2486 std::vector<ObjectID> part_ids;
2487 part_ids.reserve(parts.size());
2488 const Vec3d anchor = texture_anchor();
2489 const std::vector<float> drops = resting_drops(parts);
2490 for (size_t i = 0; i < parts.size(); ++i) {
2491 const ModelVolume *part = parts[i];
2492 TextureDisplacementPreviewVolume entry;
2493 entry.base_mesh = part->mesh().its;
2494 entry.layers = layers_for_part(*part);
2495 entry.options = owner->texture_displacement_options;
2496 // The preview is looked at, not printed or sliced: skip the simplification and the repair that
2497 // follows it, which are most of the one-run pipeline's time. The relief is the same.
2498 if (entry.options.pipeline_v2)
2499 entry.options.v2_max_triangles_k = 0;
2500 entry.volume_to_world = texture_displacement_volume_to_world(*part);
2501 entry.texture_anchor = anchor;
2502 entry.resting_drop = drops[i];
2503 if (part->has_texture_uv())
2504 entry.model_uv = part->texture_uv;
2505 for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot)
2506 entry.facets_data[size_t(slot)] = part->texture_displacement_facet(slot).get_data();
2507 input.volumes.push_back(std::move(entry));
2508 part_ids.push_back(part->id());
2509 }
2510 const ObjectID owner_id = owner->id();
2511
2512 // The result names a palette entry per triangle (index + 1), so these are the colours to draw it in:
2513 // a single filament's own, or for a mix the colour its slot will show once a bake creates it.
2514 std::vector<ColorRGBA> entry_colors;
2515 entry_colors.reserve(input.color.palette.size());
2516 for (const PrintableColor &e : input.color.palette)
2517 entry_colors.emplace_back(e.rgb.x(), e.rgb.y(), e.rgb.z(), 1.f);
2518
2519 m_preview_job_running = true;
2520 auto &worker = wxGetApp().plater()->get_ui_job_worker();
2521 queue_job(worker, std::make_unique<TextureDisplacementPreviewJob>(std::move(input), generation, m_preview_generation,
2522 [this, entry_colors, part_ids, owner_id](std::vector<TextureDisplacementPreviewResult> results,
2523 uint64_t result_generation) {
2524 m_preview_job_running = false;
2525 if (result_generation != m_preview_generation->load()) {
2526 // Superseded while this was computing (it will have aborted early and come back
2527 // empty). Whatever the newest state is, it still needs a run.
2528 m_preview_job_pending = true;
2529 } else if (results.empty()) {
2530 // Aborted or cancelled rather than finished - the handler runs on every outcome so
2531 // the in-flight latch above always clears. Keep whatever preview is already on screen
2532 // rather than blanking it; there is no new result to show, not a new empty one.
2533 } else {
2534 // A finished run carries the whole object, so it replaces the previous one outright: a
2535 // part that is no longer painted must lose its preview, or its flat volume would stay
2536 // hidden behind geometry from before.
2537 m_preview_glmodel.reset();
2538 m_preview_color_runs.clear();
2539 m_preview_its = indexed_triangle_set{};
2540 m_part_previews.clear();
2541
2542 for (size_t i = 0; i < results.size() && i < part_ids.size(); ++i) {
2543 indexed_triangle_set its = std::move(results[i].mesh);
2544 if (its.indices.empty())
2545 continue; // this part's layers moved nothing; its own volume is drawn instead
2546
2547 std::vector<PreviewColorRun> runs;
2548 indexed_triangle_set grouped =
2549 group_preview_by_color(its, results[i].triangle_color, entry_colors, runs);
2550
2551 if (part_ids[i] == owner_id) {
2552 m_preview_color_runs = std::move(runs);
2553 m_preview_glmodel.init_from(grouped);
2555 // Keep the displaced mesh so the wireframe overlay can be drawn on the raised
2556 // surface actually shown in the true-displacement view.
2557 m_preview_its = std::move(its);
2558 } else {
2559 auto preview = std::make_unique<PartPreview>();
2560 preview->volume_id = part_ids[i];
2561 preview->color_runs = std::move(runs);
2562 preview->glmodel.init_from(grouped);
2563 preview->glmodel.set_color(GLVolume::NEUTRAL_COLOR);
2564 m_part_previews.push_back(std::move(preview));
2565 }
2566 }
2567
2569 // The paint tint rides the displaced surface in this view, so it follows the new mesh.
2570 m_paint_overlay_dirty = true;
2571 }
2573 m_preview_job_pending = false;
2575 queue_preview_job(); // no-ops if the gizmo has closed in the meantime
2576 }
2577 m_parent.set_as_dirty();
2578 }));
2579}
std::vector< PreviewColorRun > m_preview_color_runs
Definition GLGizmoTextureDisplacement.hpp:196
static indexed_triangle_set group_preview_by_color(const indexed_triangle_set &its, const std::vector< uint8_t > &triangle_color, const std::vector< ColorRGBA > &entry_colors, std::vector< PreviewColorRun > &runs)
Definition GLGizmoTextureDisplacement.cpp:2429
indexed_triangle_set m_preview_its
Definition GLGizmoTextureDisplacement.hpp:1104
bool m_preview_job_running
Definition GLGizmoTextureDisplacement.hpp:1120
bool queue_job(Worker &w, ProcessFn fn, FinishFn finishfn)
Definition Worker.hpp:62

References Slic3r::GUI::TextureDisplacementPreviewVolume::base_mesh, Slic3r::GUI::TextureDisplacementPreviewInput::color, color_settings_for(), Slic3r::GUI::TextureDisplacementPreviewVolume::facets_data, Slic3r::FacetsAnnotation::get_data(), group_preview_by_color(), Slic3r::ModelVolume::has_texture_uv(), Slic3r::ObjectBase::id(), Slic3r::TriangleMesh::its, Slic3r::GUI::TextureDisplacementPreviewVolume::layers, layers_for_part(), m_paint_overlay_dirty, Slic3r::GUI::GLGizmoBase::m_parent, m_part_previews, m_preview_color_runs, m_preview_generation, m_preview_glmodel, m_preview_its, m_preview_job_pending, m_preview_job_running, Slic3r::GUI::GLGizmoBase::m_state, m_use_shaded_preview, Slic3r::ModelVolume::mesh(), Slic3r::GUI::TextureDisplacementPreviewVolume::model_uv, Slic3r::GLVolume::NEUTRAL_COLOR, Slic3r::GUI::GLGizmoBase::On, Slic3r::GUI::TextureDisplacementPreviewVolume::options, painted_parts(), Slic3r::TextureColorSettings::palette, Slic3r::TextureDisplacementOptions::pipeline_v2, Slic3r::GUI::queue_job(), queue_preview_job(), refresh_wireframe(), Slic3r::GUI::TextureDisplacementPreviewVolume::resting_drop, resting_drops(), Slic3r::PrintableColor::rgb, texture_anchor(), Slic3r::GUI::TextureDisplacementPreviewVolume::texture_anchor, Slic3r::ModelVolume::texture_displacement_facet(), Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS, Slic3r::ModelVolume::texture_displacement_options, Slic3r::texture_displacement_volume_to_world(), Slic3r::ModelVolume::texture_uv, texture_volume(), Slic3r::TextureDisplacementOptions::v2_max_triangles_k, Slic3r::GUI::TextureDisplacementPreviewVolume::volume_to_world, and Slic3r::GUI::TextureDisplacementPreviewInput::volumes.

Referenced by apply_view_mode(), queue_preview_job(), and rebuild_preview().

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

◆ rank_mixes()

std::vector< GLGizmoTextureDisplacement::PaletteEntry > Slic3r::GUI::GLGizmoTextureDisplacement::rank_mixes ( const std::vector< ColorRGBA > & filaments,
const std::vector< MixTarget > & targets,
int limit )
static
5885{
5886 const int n = int(filaments.size());
5887 if (n < 2 || targets.empty() || limit <= 0)
5888 return {};
5889
5890 // Every pair at every short-cycle ratio, coloured as its slot will be.
5891 std::vector<std::string> hex(filaments.size());
5892 for (int i = 0; i < n; ++i)
5893 hex[size_t(i)] = encode_color(filaments[size_t(i)]);
5894 std::vector<PaletteEntry> candidates;
5895 for (int i = 0; i < n; ++i)
5896 for (int j = i + 1; j < n; ++j)
5897 for (int den = 2; den <= 6; ++den)
5898 for (int num = 1; num < den; ++num) {
5899 if (std::gcd(num, den) != 1)
5900 continue; // 2/4 is 1/2, already there
5901 PaletteEntry e{ Vec3f::Zero(), i, j, num, den };
5902 ColorRGB blended;
5903 if (!decode_color(blend_color_multi({ hex[size_t(i)], hex[size_t(j)] },
5904 { e.a_percent(), 100 - e.a_percent() }),
5905 blended))
5906 continue;
5907 e.rgb = Vec3f(blended.r(), blended.g(), blended.b());
5908 candidates.push_back(e);
5909 }
5910
5911 // How far each target is from the nearest single filament, and from every candidate.
5912 const size_t nt = targets.size(), nc = candidates.size();
5913 std::vector<Vec3f> filament_lab(size_t(n), Vec3f::Zero());
5914 for (int i = 0; i < n; ++i)
5915 filament_lab[size_t(i)] = srgb_to_lab(Vec3f(filaments[size_t(i)].r(), filaments[size_t(i)].g(), filaments[size_t(i)].b()));
5916 const auto de = [](const Vec3f &l, const Vec3f &r) { return DeltaE00(l.x(), l.y(), l.z(), r.x(), r.y(), r.z()); };
5917 std::vector<float> pure_d(nt, std::numeric_limits<float>::max());
5918 for (size_t t = 0; t < nt; ++t)
5919 for (const Vec3f &lab : filament_lab)
5920 pure_d[t] = std::min(pure_d[t], de(targets[t].lab, lab));
5921 std::vector<float> dist(nc * nt);
5922 tbb::parallel_for(tbb::blocked_range<size_t>(0, nc), [&](const tbb::blocked_range<size_t> &range) {
5923 for (size_t c = range.begin(); c < range.end(); ++c) {
5924 const Vec3f lab = srgb_to_lab(candidates[c].rgb);
5925 for (size_t t = 0; t < nt; ++t)
5926 dist[c * nt + t] = de(targets[t].lab, lab);
5927 }
5928 });
5929
5930 // Greedy: each round takes the candidate that lowers the weighted error the most. A candidate only
5931 // counts where it beats the single filament by PREFER_PURE_DE, since everywhere else the quantizer
5932 // picks the filament anyway.
5933 std::vector<float> current = pure_d;
5934 std::vector<char> taken(nc, 0);
5935 std::vector<PaletteEntry> out;
5936 const auto counts = [&](size_t c, size_t t) {
5937 const float d = dist[c * nt + t];
5938 return d < current[t] && d <= pure_d[t] - PREFER_PURE_DE;
5939 };
5940 while (int(out.size()) < limit) {
5941 size_t best = nc;
5942 double best_gain = 0.;
5943 for (size_t c = 0; c < nc; ++c) {
5944 if (taken[c])
5945 continue;
5946 double gain = 0.;
5947 for (size_t t = 0; t < nt; ++t)
5948 if (counts(c, t))
5949 gain += double(targets[t].weight) * double(current[t] - dist[c * nt + t]);
5950 if (gain > best_gain) {
5951 best_gain = gain;
5952 best = c;
5953 }
5954 }
5955 if (best == nc || best_gain < double(MIN_MIX_GAIN))
5956 break;
5957 taken[best] = 1;
5958 out.push_back(candidates[best]);
5959 for (size_t t = 0; t < nt; ++t)
5960 if (counts(best, t))
5961 current[t] = dist[best * nt + t];
5962 }
5963 return out;
5964}
constexpr float PREFER_PURE_DE
Definition GLGizmoTextureDisplacement.cpp:332
static wxColour decode_color(const std::string &color)
Definition SliceInfoPanel.cpp:51
std::string encode_color(const ColorRGB &color)
Definition Color.cpp:397
std::string blend_color_multi(const std::vector< std::string > &hex_colors, const std::vector< int > &weights)
Definition FilamentMixer.cpp:124

References Slic3r::PrintableColor::a_percent(), Slic3r::ColorRGB::b(), Slic3r::blend_color_multi(), Slic3r::GUI::decode_color(), DeltaE00(), Slic3r::Geometry::dist(), Slic3r::encode_color(), Slic3r::ColorRGB::g(), Slic3r::ColorRGB::r(), Slic3r::range(), and Slic3r::PrintableColor::rgb.

Referenced by cached_palette().

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

◆ rebuild_island_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_island_overlay ( const std::vector< int > & selection)
private
2076{
2078 m_island_overlay_selection = selection;
2079 const ModelVolume *mv = texture_volume();
2080 if (mv == nullptr || selection.empty() || m_uv_editor_unwrap.empty())
2081 return;
2082 // The unwrap was made from the painted patch in the bake frame; the same extraction on the
2083 // volume's own mesh gives the same triangles and vertex order in local coordinates, which is the
2084 // frame the overlay is drawn in (with the volume's transform, like the paint tint).
2085 const indexed_triangle_set patch = extract_painted_patch(mv->mesh().its, m_uv_editor_state.facets);
2086 const PatchUnwrap &uw = m_uv_editor_unwrap;
2087 std::vector<uint8_t> chosen(size_t(std::max(uw.chart_count, 0)), 0);
2088 for (const int c : selection)
2089 if (c >= 0 && size_t(c) < chosen.size())
2090 chosen[size_t(c)] = 1;
2091
2092 GLModel::Geometry init_data;
2094 unsigned n = 0;
2095 for (const stl_triangle_vertex_indices &tri : uw.indices) {
2096 const int v0 = tri[0];
2097 if (v0 < 0 || size_t(v0) >= uw.vertex_chart.size())
2098 continue;
2099 const int c = uw.vertex_chart[size_t(v0)];
2100 if (c < 0 || size_t(c) >= chosen.size() || !chosen[size_t(c)])
2101 continue;
2102 bool ok = true;
2103 for (int k = 0; k < 3 && ok; ++k) {
2104 const int u = tri[k];
2105 ok = u >= 0 && size_t(u) < uw.source_vertex.size() && uw.source_vertex[size_t(u)] >= 0 &&
2106 size_t(uw.source_vertex[size_t(u)]) < patch.vertices.size();
2107 }
2108 if (!ok)
2109 continue;
2110 for (int k = 0; k < 3; ++k)
2111 init_data.add_vertex(patch.vertices[size_t(uw.source_vertex[size_t(tri[k])])]);
2112 init_data.add_triangle(n, n + 1, n + 2);
2113 n += 3;
2114 }
2115 if (n == 0)
2116 return;
2117 m_island_overlay_glmodel.init_from(std::move(init_data));
2118 m_island_overlay_glmodel.set_color(ColorRGBA(0.10f, 0.55f, 0.95f, 0.45f)); // the pane's selection blue
2119}
GLModel m_island_overlay_glmodel
Definition GLGizmoTextureDisplacement.hpp:926
std::vector< int > m_island_overlay_selection
Definition GLGizmoTextureDisplacement.hpp:927
indexed_triangle_set extract_painted_patch(const indexed_triangle_set &base_mesh, const TriangleSelector::TriangleSplittingData &facet_data)
Definition TextureDisplacement.cpp:2457

References Slic3r::GUI::GLModel::Geometry::add_triangle(), Slic3r::GUI::GLModel::Geometry::add_vertex(), Slic3r::PatchUnwrap::chart_count, Slic3r::extract_painted_patch(), Slic3r::GUI::GLModel::Geometry::format, Slic3r::PatchUnwrap::indices, Slic3r::TriangleMesh::its, m_island_overlay_glmodel, m_island_overlay_selection, m_uv_editor_state, m_uv_editor_unwrap, Slic3r::ModelVolume::mesh(), Slic3r::GUI::GLModel::Geometry::P3, Slic3r::PatchUnwrap::source_vertex, texture_volume(), Slic3r::GUI::GLModel::Geometry::Triangles, and Slic3r::PatchUnwrap::vertex_chart.

Referenced by render_painter_gizmo().

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

◆ rebuild_other_paint_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_other_paint_overlay ( )
private
1812{
1813 const ModelVolume *mv = texture_volume();
1814 // What it depends on: the volume, which layer is active, every other layer's paint (by its timestamp) and the
1815 // displaced positions it is lifted onto. Compared every frame, rebuilt only when it differs.
1816 std::string key;
1817 if (mv != nullptr) {
1818 key = std::to_string(mv->id().id) + ":" + std::to_string(m_active_layer_slot) + (m_use_shaded_preview ? ":b:" : ":t:") +
1819 std::to_string(reinterpret_cast<uintptr_t>(m_preview_its.vertices.data())) + ":" +
1820 std::to_string(m_preview_its.vertices.size());
1821 for (const TextureDisplacementLayer &l : mv->texture_displacement_layers)
1822 if (l.slot != m_active_layer_slot && l.slot >= 0 && l.slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS))
1823 key += "|" + std::to_string(l.slot) + "@" + std::to_string(mv->texture_displacement_facet(l.slot).timestamp());
1824 }
1825 if (key == m_other_paint_key)
1826 return;
1827 m_other_paint_key = std::move(key);
1828 m_other_paint_glmodel.reset();
1829 if (mv == nullptr)
1830 return;
1831
1832 const std::vector<Vec3f> *displaced = nullptr;
1833 if (!m_use_shaded_preview && m_preview_its.vertices.size() == mv->mesh().its.vertices.size() &&
1834 !m_preview_its.vertices.empty())
1835 displaced = &m_preview_its.vertices;
1836
1837 GLModel::Geometry init_data;
1839 for (const TextureDisplacementLayer &l : mv->texture_displacement_layers) {
1840 if (l.slot == m_active_layer_slot || l.slot < 0 || l.slot >= int(TEXTURE_DISPLACEMENT_MAX_LAYERS) ||
1841 mv->texture_displacement_facet(l.slot).empty())
1842 continue;
1843 TriangleSelector selector(mv->mesh());
1844 selector.deserialize(mv->texture_displacement_facet(l.slot).get_data(), false);
1845 append_paint_patch(init_data, selector.get_facets_strict(EnforcerBlockerType::ENFORCER), displaced);
1846 }
1847 if (init_data.is_empty())
1848 return;
1849 m_other_paint_glmodel.init_from(std::move(init_data));
1850 // Neutral grey: painted, but not the layer the brush is working on.
1851 m_other_paint_glmodel.set_color(ColorRGBA(0.55f, 0.58f, 0.60f, 0.35f));
1852}
std::string m_other_paint_key
Definition GLGizmoTextureDisplacement.hpp:940
GLModel m_other_paint_glmodel
Definition GLGizmoTextureDisplacement.hpp:939
static void append_paint_patch(GLModel::Geometry &out, const indexed_triangle_set &patch, const std::vector< Vec3f > *displaced)
Definition GLGizmoTextureDisplacement.cpp:1798

References Slic3r::GUI::append_paint_patch(), Slic3r::TriangleSelector::deserialize(), Slic3r::FacetsAnnotation::empty(), Slic3r::ENFORCER, Slic3r::GUI::GLModel::Geometry::format, Slic3r::FacetsAnnotation::get_data(), Slic3r::TriangleSelector::get_facets_strict(), Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, Slic3r::GUI::GLModel::Geometry::is_empty(), Slic3r::TriangleMesh::its, m_active_layer_slot, m_other_paint_glmodel, m_other_paint_key, m_preview_its, m_use_shaded_preview, Slic3r::ModelVolume::mesh(), Slic3r::GUI::GLModel::Geometry::P3, Slic3r::TextureDisplacementLayer::slot, Slic3r::ModelVolume::texture_displacement_facet(), Slic3r::ModelVolume::texture_displacement_layers, Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS, texture_volume(), Slic3r::ObjectWithTimestamp::timestamp(), and Slic3r::GUI::GLModel::Geometry::Triangles.

Referenced by render_painter_gizmo().

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

◆ rebuild_paint_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_paint_overlay ( )
private
1999{
2001 const ModelVolume *mv = texture_volume();
2002 if (mv == nullptr || m_triangle_selectors.empty())
2003 return;
2004
2005 // The *live* selector, so an in-progress stroke shows immediately - which is the whole point:
2006 // this is the only feedback that a brush actually added or erased anything until the (much more
2007 // expensive) preview catches up at stroke end.
2008 const indexed_triangle_set patch = m_triangle_selectors[0]->get_facets_strict(EnforcerBlockerType::ENFORCER);
2009 if (patch.indices.empty())
2010 return;
2011
2012 // In the true-displacement view the surface on screen is the *raised* one, and a tint built on
2013 // the flat base mesh would sink underneath it wherever the relief is deepest - which is precisely
2014 // where the user is looking. The bake is topology-preserving (patch vertex i is mesh vertex i, see
2015 // build_texture_displacement()), so the displaced positions can be read straight across. Vertices
2016 // the brush split live past the end of that array and keep their flat position; they sit on the
2017 // patch boundary, where the displacement is smallest anyway.
2018 const std::vector<Vec3f> *displaced = nullptr;
2019 if (!m_use_shaded_preview && m_preview_its.vertices.size() == mv->mesh().its.vertices.size() &&
2020 !m_preview_its.vertices.empty())
2021 displaced = &m_preview_its.vertices;
2022
2023 GLModel::Geometry init_data;
2025 init_data.reserve_vertices(patch.indices.size() * 3);
2026 init_data.reserve_indices(patch.indices.size() * 3);
2027 append_paint_patch(init_data, patch, displaced);
2028 m_paint_overlay_glmodel.init_from(std::move(init_data));
2029 // GLModel::render() re-sets "uniform_color" from this field just before drawing, so the colour
2030 // has to be set here rather than as a uniform at draw time.
2031 m_paint_overlay_glmodel.set_color(ColorRGBA(0.16f, 0.79f, 0.35f, 0.38f));
2032}

References Slic3r::GUI::append_paint_patch(), Slic3r::ENFORCER, Slic3r::GUI::GLModel::Geometry::format, Slic3r::TriangleMesh::its, m_paint_overlay_glmodel, m_preview_its, Slic3r::GUI::GLGizmoPainterBase::m_triangle_selectors, m_use_shaded_preview, Slic3r::ModelVolume::mesh(), Slic3r::GUI::GLModel::Geometry::P3, Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), texture_volume(), and Slic3r::GUI::GLModel::Geometry::Triangles.

Referenced by rebuild_preview(), and render_painter_gizmo().

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

◆ rebuild_painted_colors()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_painted_colors ( bool whole_stack)
private
1886{
1887 const ModelVolume *mv = texture_volume();
1888 const bool shown = mv != nullptr && any_layer_colors(*mv) && !mv->mmu_segmentation_facets.empty();
1889
1890 // The sub-triangles, keyed on what they were read from: the volume, its mesh and the paint.
1891 std::string key;
1892 if (shown)
1893 key = std::to_string(mv->id().id) + ":" + std::to_string(reinterpret_cast<uintptr_t>(mv->mesh_ptr().get())) + ":" +
1894 std::to_string(mv->mmu_segmentation_facets.timestamp());
1895 if (key != m_painted_colors_key) {
1896 m_painted_colors_key = std::move(key);
1897 m_painted_colors = shown ? painted_colors(mv->mesh(), mv->mmu_segmentation_facets.get_data()) : PaintedColors{};
1900 m_painted_colors_runs.clear();
1901 }
1902 if (m_painted_colors.state.empty())
1903 return;
1904
1905 // The part drawn, keyed on whose paint is left out and on that paint.
1906 std::string drawn_key = m_painted_colors_key + (whole_stack ? std::string(":all") : ":" + std::to_string(m_active_layer_slot));
1907 for (const TextureDisplacementLayer &l : mv->texture_displacement_layers)
1908 if (l.slot >= 0 && l.slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS))
1909 drawn_key += "|" + std::to_string(l.slot) + "@" + std::to_string(mv->texture_displacement_facet(l.slot).timestamp());
1910 if (drawn_key == m_painted_colors_drawn_key)
1911 return;
1912 m_painted_colors_drawn_key = std::move(drawn_key);
1914 m_painted_colors_runs.clear();
1915
1916 // Whole model triangles, as the facets record what they touch: a triangle the paint only partly covers
1917 // is left to the preview.
1918 std::vector<bool> excluded(mv->mesh().its.indices.size(), false);
1919 for (const TextureDisplacementLayer &l : mv->texture_displacement_layers) {
1920 if (l.slot < 0 || l.slot >= int(TEXTURE_DISPLACEMENT_MAX_LAYERS) || (!whole_stack && l.slot != m_active_layer_slot))
1921 continue;
1922 for (const TriangleSelector::TriangleBitStreamMapping &m : mv->texture_displacement_facet(l.slot).get_data().triangles_to_split)
1923 if (m.triangle_idx >= 0 && size_t(m.triangle_idx) < excluded.size())
1924 excluded[size_t(m.triangle_idx)] = true;
1925 }
1926
1927 // One model, its triangles in filament order (painted_colors() groups them), drawn a range per filament.
1928 const std::vector<size_t> kept = m_painted_colors.outside(excluded);
1929 GLModel::Geometry init_data;
1931 init_data.reserve_vertices(kept.size() * 3);
1932 init_data.reserve_indices(kept.size() * 3);
1933 unsigned n = 0;
1934 for (const size_t t : kept) {
1935 const int state = m_painted_colors.state[t];
1936 if (m_painted_colors_runs.empty() || m_painted_colors_runs.back().first != state)
1937 m_painted_colors_runs.push_back({ state, { size_t(n), size_t(n) } });
1938 for (int i = 0; i < 3; ++i)
1939 init_data.add_vertex(m_painted_colors.facets.vertices[size_t(m_painted_colors.facets.indices[t][i])]);
1940 init_data.add_triangle(n, n + 1, n + 2);
1941 n += 3;
1942 m_painted_colors_runs.back().second.second = size_t(n);
1943 }
1944 if (!init_data.is_empty())
1945 m_painted_colors_glmodel.init_from(std::move(init_data));
1946}
static PaintedColors painted_colors(const TriangleMesh &mesh, const TriangleSelector::TriangleSplittingData &paint)
Definition GLGizmoTextureDisplacement.cpp:1864

References Slic3r::GUI::GLModel::Geometry::add_triangle(), Slic3r::GUI::GLModel::Geometry::add_vertex(), any_layer_colors(), Slic3r::FacetsAnnotation::empty(), Slic3r::GUI::GLModel::Geometry::format, Slic3r::FacetsAnnotation::get_data(), Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, Slic3r::GUI::GLModel::Geometry::is_empty(), Slic3r::TriangleMesh::its, m_active_layer_slot, m_painted_colors, m_painted_colors_drawn_key, m_painted_colors_glmodel, m_painted_colors_key, m_painted_colors_runs, Slic3r::ModelVolume::mesh(), Slic3r::ModelVolume::mesh_ptr(), Slic3r::ModelVolume::mmu_segmentation_facets, Slic3r::GUI::GLModel::Geometry::P3, painted_colors(), Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), Slic3r::TextureDisplacementLayer::slot, Slic3r::ModelVolume::texture_displacement_facet(), Slic3r::ModelVolume::texture_displacement_layers, Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS, texture_volume(), Slic3r::ObjectWithTimestamp::timestamp(), Slic3r::GUI::GLModel::Geometry::Triangles, and Slic3r::TriangleSelector::TriangleSplittingData::triangles_to_split.

Referenced by render_painted_colors().

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

◆ rebuild_preview()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_preview ( )
private
2379{
2380 // Raised first: any in-flight job's result (captured generation from before this call) will
2381 // now compare unequal to m_preview_generation and be discarded when it completes, even if it
2382 // finishes after the job queued below - and, since the counter is shared with the worker, that
2383 // job also notices mid-run and aborts rather than computing a result nobody will use.
2384 m_preview_generation->fetch_add(1);
2385 // Everything rebuilt from here on reads the current palette. Cleared before any of the early returns
2386 // below, which would otherwise leave it set and re-run this every frame.
2387 m_palette_changed = false;
2393 // The adaptive subdivision preview is driven by the painted area, so it has to follow the paint
2394 // while it is open - a plain stroke changes which triangles would be refined. The uniform preview
2395 // depends only on the mesh, so it is left to its own controls.
2398
2399 // The debug view owns m_preview_glmodel while it is up. A stage is a snapshot of a run that has
2400 // already finished, so letting the live preview overwrite it would replace the thing being
2401 // inspected with something else; leaving the view is explicit (its Close button).
2402 if (m_debug_stage >= 0)
2403 return;
2404
2405 const ModelVolume *mv = texture_volume();
2406 if (mv == nullptr || !any_part_painted()) {
2407 m_preview_glmodel.reset();
2408 m_part_previews.clear();
2409 m_preview_its = indexed_triangle_set{}; // no displaced mesh; wireframe falls back to the base
2410 m_preview_job_pending = false;
2412 return;
2413 }
2414 // In Fast/paint modes the wireframe follows the base mesh and can be built now; the true-displacement
2415 // view's wireframe needs the displaced mesh, which only exists once the job below completes.
2418 // Fast view: the shader *is* the preview, and m_preview_glmodel is never drawn. Running the
2419 // full CPU displacement anyway - which is what happened on every stroke and slider release -
2420 // was the single largest cost in the gizmo, and it bought nothing. The switch back to the
2421 // true-displacement view queues it (see the View row in on_render_input_window()).
2422 m_preview_job_pending = false;
2423 return;
2424 }
2425
2427}
void rebuild_seam_overlay()
Definition GLGizmoTextureDisplacement.cpp:1167
void rebuild_paint_overlay()
Definition GLGizmoTextureDisplacement.cpp:1998

References any_part_painted(), m_debug_stage, m_palette_changed, m_part_previews, m_preview_generation, m_preview_glmodel, m_preview_its, m_preview_job_pending, m_subdivide_adaptive, m_subdivide_editing, m_use_shaded_preview, queue_preview_job(), rebuild_paint_overlay(), rebuild_seam_overlay(), rebuild_shaded_preview_mesh(), rebuild_subdivide_preview(), rebuild_uvcheck_mesh(), refresh_wireframe(), texture_volume(), and update_uv_editor().

Referenced by add_texture_layer(), apply_projection_frame(), close_image_adjust(), exit_debug_view(), mark_seam_path(), on_island_edited(), on_mouse_adjust_texture(), on_uv_vertex_edited(), remove_texture_layer(), run_uv_command(), set_active_layer(), set_layer_texture(), toggle_seam_at(), update_from_model_object(), and update_model_object().

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

◆ rebuild_seam_anchor_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_seam_anchor_overlay ( )
private
1097{
1098 m_seam_anchor_glmodel.reset();
1099 const ModelVolume *mv = texture_volume();
1100 if (!m_seam_edit_mode || !m_seam_path_mode || mv == nullptr || m_seam_path_anchor < 0)
1101 return;
1102 const indexed_triangle_set &its = mv->mesh().its;
1103 if (size_t(m_seam_path_anchor) >= its.vertices.size())
1104 return;
1105
1106 // The anchor's incident edges, so the path's start vertex is visible on the model.
1107 GLModel::Geometry init_data;
1109 unsigned nn = 0;
1110 for (const stl_triangle_vertex_indices &tri : its.indices)
1111 for (int i = 0; i < 3; ++i) {
1112 const int a = tri[i], b = tri[(i + 1) % 3];
1113 if (a == m_seam_path_anchor || b == m_seam_path_anchor) {
1114 init_data.add_vertex(its.vertices[size_t(a)]);
1115 init_data.add_vertex(its.vertices[size_t(b)]);
1116 init_data.add_line(nn, nn + 1);
1117 nn += 2;
1118 }
1119 }
1120 if (!init_data.is_empty())
1121 m_seam_anchor_glmodel.init_from(std::move(init_data));
1122}

References Slic3r::GUI::GLModel::Geometry::add_line(), Slic3r::GUI::GLModel::Geometry::add_vertex(), Slic3r::GUI::GLModel::Geometry::format, Slic3r::GUI::GLModel::Geometry::is_empty(), Slic3r::TriangleMesh::its, Slic3r::GUI::GLModel::Geometry::Lines, m_seam_anchor_glmodel, m_seam_edit_mode, m_seam_path_anchor, m_seam_path_mode, Slic3r::ModelVolume::mesh(), Slic3r::GUI::GLModel::Geometry::P3, and texture_volume().

Referenced by on_mouse_seam().

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

◆ rebuild_seam_hover_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_seam_hover_overlay ( )
private
1125{
1126 m_seam_hover_glmodel.reset();
1127 const ModelVolume *mv = texture_volume();
1128 if (!m_seam_edit_mode || mv == nullptr)
1129 return;
1130 const indexed_triangle_set &its = mv->mesh().its;
1131
1132 GLModel::Geometry init_data;
1134
1135 if (m_seam_path_mode) {
1136 // Highlight the hovered vertex as its ring of incident edges, so the click target is legible on
1137 // a dense mesh (matching the green anchor's style, in the hover yellow render_seam_overlay uses).
1138 if (m_seam_hover_vertex < 0 || size_t(m_seam_hover_vertex) >= its.vertices.size())
1139 return;
1140 unsigned nn = 0;
1141 for (const stl_triangle_vertex_indices &tri : its.indices)
1142 for (int i = 0; i < 3; ++i) {
1143 const int a = tri[i], b = tri[(i + 1) % 3];
1144 if (a == m_seam_hover_vertex || b == m_seam_hover_vertex) {
1145 init_data.add_vertex(its.vertices[size_t(a)]);
1146 init_data.add_vertex(its.vertices[size_t(b)]);
1147 init_data.add_line(nn, nn + 1);
1148 nn += 2;
1149 }
1150 }
1151 if (!init_data.is_empty())
1152 m_seam_hover_glmodel.init_from(std::move(init_data));
1153 return;
1154 }
1155
1156 if (m_seam_hover_edge.first < 0 || size_t(m_seam_hover_edge.first) >= its.vertices.size() ||
1157 size_t(m_seam_hover_edge.second) >= its.vertices.size())
1158 return;
1159 init_data.reserve_vertices(2);
1160 init_data.reserve_indices(2);
1161 init_data.add_vertex(its.vertices[size_t(m_seam_hover_edge.first)]);
1162 init_data.add_vertex(its.vertices[size_t(m_seam_hover_edge.second)]);
1163 init_data.add_line(0, 1);
1164 m_seam_hover_glmodel.init_from(std::move(init_data));
1165}

References Slic3r::GUI::GLModel::Geometry::add_line(), Slic3r::GUI::GLModel::Geometry::add_vertex(), Slic3r::GUI::GLModel::Geometry::format, Slic3r::GUI::GLModel::Geometry::is_empty(), Slic3r::TriangleMesh::its, Slic3r::GUI::GLModel::Geometry::Lines, m_seam_edit_mode, m_seam_hover_edge, m_seam_hover_glmodel, m_seam_hover_vertex, m_seam_path_mode, Slic3r::ModelVolume::mesh(), Slic3r::GUI::GLModel::Geometry::P3, Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), and texture_volume().

Referenced by on_mouse_seam().

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

◆ rebuild_seam_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_seam_overlay ( )
private
1168{
1169 m_seam_glmodel.reset();
1170 const ModelVolume *mv = texture_volume();
1171 const TextureDisplacementLayer *layer = active_layer();
1172 if (mv == nullptr || layer == nullptr || layer->lscm_seam_edges.empty())
1173 return;
1174 const indexed_triangle_set &its = mv->mesh().its;
1175
1176 GLModel::Geometry init_data;
1178 init_data.reserve_vertices(layer->lscm_seam_edges.size() * 2);
1179 init_data.reserve_indices(layer->lscm_seam_edges.size() * 2);
1180 unsigned n = 0;
1181 for (const auto &[a, b] : layer->lscm_seam_edges) {
1182 if (a < 0 || b < 0 || size_t(a) >= its.vertices.size() || size_t(b) >= its.vertices.size())
1183 continue;
1184 init_data.add_vertex(its.vertices[size_t(a)]);
1185 init_data.add_vertex(its.vertices[size_t(b)]);
1186 init_data.add_line(n, n + 1);
1187 n += 2;
1188 }
1189 if (!init_data.is_empty())
1190 m_seam_glmodel.init_from(std::move(init_data));
1191}

References active_layer(), Slic3r::GUI::GLModel::Geometry::add_line(), Slic3r::GUI::GLModel::Geometry::add_vertex(), Slic3r::GUI::GLModel::Geometry::format, Slic3r::GUI::GLModel::Geometry::is_empty(), Slic3r::TriangleMesh::its, Slic3r::GUI::GLModel::Geometry::Lines, Slic3r::TextureDisplacementLayer::lscm_seam_edges, m_seam_glmodel, Slic3r::ModelVolume::mesh(), Slic3r::GUI::GLModel::Geometry::P3, Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), and texture_volume().

Referenced by rebuild_preview().

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

◆ rebuild_shaded_preview_mesh()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_shaded_preview_mesh ( )
private
1449{
1451
1452 const ModelVolume *mv = texture_volume();
1453 if (mv == nullptr || m_triangle_selectors.empty())
1454 return;
1455
1456 // Uses the *live* selector (not the flushed model facet data), so this reflects an in-progress
1457 // stroke immediately rather than only once it ends - the point of this preview mode is to be
1458 // the fast, no-CPU-meshing one.
1459 // The source map comes along because a ModelUV layer's uvs are read per base-mesh triangle; every
1460 // other mapping ignores it.
1461 std::vector<int> patch_source;
1462 const indexed_triangle_set patch =
1463 m_triangle_selectors[0]->get_facets_strict(EnforcerBlockerType::ENFORCER, &patch_source);
1464 if (patch.indices.empty())
1465 return;
1466
1467 // No "patch.vertices.size() == mesh vertex count" check here, and that is the point: a *brush*
1468 // stroke splits triangles, so the selector appends split vertices and the patch array is longer
1469 // than the mesh's. An earlier version bailed out on that as "shouldn't happen", which meant the
1470 // shaded model was never built while brushing and render_painter_gizmo() silently fell back to the
1471 // Normal (true-displacement) preview - Fast looked broken for brush and fine for Face/Connected
1472 // area, because only the brush splits. Everything below indexes the patch's own vertex array, so
1473 // the extra vertices are simply carried through.
1474
1475 // Unpainted triangles, so the surrounding surface still renders (the render path hides the real
1476 // model in shaded mode). get_facets_strict() returns the same vertex array whatever state is asked.
1477 const indexed_triangle_set rest = m_triangle_selectors[0]->get_facets_strict(EnforcerBlockerType::NONE);
1478
1479 // For LSCM we hand the shader the finished per-vertex texture uv (island placement + tiling/
1480 // rotation/offset already folded in, exactly what the bake samples), because it cannot be
1481 // reconstructed in the fragment shader the way a triplanar projection can. This is also what
1482 // makes the fast preview follow the UV editor: the uvs move when an island is dragged, so this
1483 // mesh rebuilds (on drag end) with them. The other projections keep projecting in-shader.
1484 // Per *corner*, not per vertex: the mesh below is flat (unshared) anyway, so each triangle can
1485 // carry its own chart's UVs - see compute_layer_corner_uvs().
1486 const TextureDisplacementLayer *active = active_layer();
1487 std::vector<Vec2f> corner_uv = active != nullptr ? compute_layer_corner_uvs(patch, *active, &patch_source) :
1488 std::vector<Vec2f>{};
1489 m_shaded_preview_uses_vertex_uv = corner_uv.size() == patch.indices.size() * 3;
1491 corner_uv.clear();
1494
1495 // Which triangles the in-flight UV drag moves. Computed here, against the very patch this mesh is
1496 // built from, so the flags can never be indexed by a different triangle count than they were sized
1497 // for (the drag starts from the flushed facet data, a brush stroke changes the live selector).
1498 compute_shaded_active_faces(m_shaded_active_chart >= 0 ? m_island_move_set : std::vector<int>{}, patch.indices.size());
1499
1500 // Colour is quantized per *fragment* in the shader now (see the .fs), so this mesh carries no
1501 // colour of its own - the palette and the colour texture are uniforms, and every pixel matches the
1502 // image rather than the facet it landed on. What the *bake* will produce, at facet resolution, is
1503 // what the Normal view shows.
1504 m_shaded_preview_palette = (active != nullptr && active->color_enabled) ? cached_palette()
1505 : std::vector<PaletteEntry>{};
1506
1507 GLModel::Geometry init_data;
1508 // P3N3T2: normal.x carries the paint weight, tex_coord the precomputed uv (see the vertex shader).
1510
1511 // Per-triangle weighting via a *flat* (unshared-vertex) mesh: every corner of a painted triangle
1512 // gets weight 1, every corner of an unpainted one weight 0. This is what a coarse mesh needs - one
1513 // painted face of a raw cube has no strictly-interior vertex (all 8 are shared), so per-vertex
1514 // weighting would either bleed onto the neighbours (boundary weight 1) or vanish outright (boundary
1515 // weight 0, which is what made a single face show nothing). Duplicating vertices costs no shading
1516 // quality here because the preview shader takes its surface normal from screen-space derivatives of
1517 // position (dFdx/dFdy), not from a per-vertex normal. normal.y flags the UV-editor island being
1518 // dragged so the shader can move just that island via the island_delta uniform.
1519 const size_t tri_total = patch.indices.size() + rest.indices.size();
1520 init_data.reserve_vertices(tri_total * 3);
1521 init_data.reserve_indices(tri_total * 3);
1522 unsigned vcount = 0;
1523 const auto emit_triangles = [&](const indexed_triangle_set &its, float weight, bool painted) {
1524 for (size_t f = 0; f < its.indices.size(); ++f) {
1525 const stl_triangle_vertex_indices &tri = its.indices[f];
1526 // One value for the whole triangle: island_active is an interpolated varying, so the three
1527 // corners have to agree or the shader moves part of a triangle and not the rest.
1528 const float act = (painted && f < m_shaded_active_face.size() && m_shaded_active_face[f]) ? 1.f : 0.f;
1529 for (int i = 0; i < 3; ++i) {
1530 const int idx = tri[i];
1531 const Vec2f uv = (painted && m_shaded_preview_uses_vertex_uv) ? corner_uv[f * 3 + size_t(i)]
1532 : Vec2f::Zero();
1533 init_data.add_vertex(its.vertices[size_t(idx)], Vec3f(weight, act, 0.f), uv);
1534 }
1535 init_data.add_triangle(vcount, vcount + 1, vcount + 2);
1536 vcount += 3;
1537 }
1538 };
1539 emit_triangles(patch, 1.f, true); // painted -> shaded as relief, and coloured by the shader
1540 // Untouched surface: flat, so it still shows but carries no relief - and uncoloured, which is what the
1541 // bake leaves it as (EnforcerBlockerType::NONE, i.e. the volume's own filament).
1542 emit_triangles(rest, 0.f, false);
1543
1544 m_shaded_preview_glmodel.init_from(std::move(init_data));
1545 // GLModel::render() unconditionally re-sets the shader's "uniform_color" from this internal
1546 // color field right before drawing (see GLModel.cpp) - setting the uniform manually in
1547 // render_shaded_preview_mesh() would just get overwritten by it, so it must be set here instead.
1548 // GLModel::Geometry defaults to BLACK, which is exactly what showed up before this was added.
1550
1551 // The mesh now reflects the islands' current placement, so any live drag delta is measured from
1552 // here: reset it to identity and record the dragged island's baked transform.
1553 m_shaded_island_delta = Eigen::Matrix<float, 2, 3>::Identity();
1554 const TextureDisplacementLayer *al = active_layer();
1555 if (m_shaded_active_chart >= 0 && al != nullptr) {
1556 const std::vector<Eigen::Matrix<float, 2, 3>> xf = uv_editor_island_transforms(*al);
1557 m_shaded_baked_active_xf = (size_t(m_shaded_active_chart) < xf.size()) ? xf[size_t(m_shaded_active_chart)]
1558 : Eigen::Matrix<float, 2, 3>::Identity();
1559 } else {
1560 m_shaded_baked_active_xf = Eigen::Matrix<float, 2, 3>::Identity();
1561 }
1562}
Vec3f m_shaded_patch_axis
Definition GLGizmoTextureDisplacement.hpp:971
std::vector< PaletteEntry > m_shaded_preview_palette
Definition GLGizmoTextureDisplacement.hpp:975
std::vector< Vec2f > compute_layer_corner_uvs(const indexed_triangle_set &patch, const TextureDisplacementLayer &layer, const std::vector< int > *patch_source=nullptr) const
Definition GLGizmoTextureDisplacement.cpp:1423
void compute_shaded_active_faces(const std::vector< int > &charts, size_t patch_face_count)
Definition GLGizmoTextureDisplacement.cpp:1564
int layer_projection_frame(const indexed_triangle_set &local_patch, const TextureDisplacementLayer &layer, Vec3f &center, Vec3f &axis) const
Definition GLGizmoTextureDisplacement.cpp:1402
Vec3f m_shaded_patch_center
Definition GLGizmoTextureDisplacement.hpp:970
bool m_shaded_preview_uses_vertex_uv
Definition GLGizmoTextureDisplacement.hpp:965
int m_shaded_projection_mode
Definition GLGizmoTextureDisplacement.hpp:969

References active_layer(), Slic3r::GUI::GLModel::Geometry::add_triangle(), Slic3r::GUI::GLModel::Geometry::add_vertex(), cached_palette(), Slic3r::TextureDisplacementLayer::color_enabled, compute_layer_corner_uvs(), compute_shaded_active_faces(), Slic3r::ENFORCER, Slic3r::f(), Slic3r::GUI::GLModel::Geometry::format, layer_projection_frame(), m_island_move_set, m_shaded_active_chart, m_shaded_active_face, m_shaded_baked_active_xf, m_shaded_island_delta, m_shaded_patch_axis, m_shaded_patch_center, m_shaded_preview_glmodel, m_shaded_preview_palette, m_shaded_preview_uses_vertex_uv, m_shaded_projection_mode, Slic3r::GUI::GLGizmoPainterBase::m_triangle_selectors, Slic3r::GLVolume::NEUTRAL_COLOR, Slic3r::NONE, Slic3r::GUI::GLModel::Geometry::P3N3T2, Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), texture_volume(), Slic3r::GUI::GLModel::Geometry::Triangles, and uv_editor_island_transforms().

Referenced by apply_view_mode(), rebuild_preview(), and render_painter_gizmo().

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

◆ rebuild_subdivide_preview()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_subdivide_preview ( )
private
6458{
6461 const ModelVolume *mv = texture_volume();
6462 if (mv == nullptr)
6463 return;
6464
6465 // The same subdivision Apply would commit, kept in a throwaway mesh and shown only as a
6466 // wireframe - the model itself is not touched until Apply.
6467 indexed_triangle_set its;
6469 if (m_subdivide_target_mm <= 0.f)
6470 return;
6472 std::vector<uint8_t> region;
6473 if (!collect_paint_region(mv->mesh(), facets, region, nullptr))
6474 return; // nothing painted yet: nothing to preview
6475 HeightFieldSampler sampler;
6477 sampler = make_combined_displacement_sampler(mv->mesh().its, mv->texture_displacement_layers, facets);
6478 // Feature mode: curvature (Detail tolerance) on top of the Max-edge baseline, down to the
6479 // Min-edge floor. Plain adaptive: tol 0, so only the target-edge-length criterion applies.
6480 const float tol = m_subdivide_feature ? m_subdivide_detail_mm : 0.f;
6481 const float floor = m_subdivide_feature ? m_subdivide_min_edge_mm : 0.f;
6482 // Same colour criterion Apply will use, so the previewed wireframe is the mesh that commits.
6483 ColorFieldSampler color;
6484 if (m_subdivide_color_mm > 0.f && any_layer_colors(*mv)) {
6485 const auto [quantize, quantize_pure] = palette_quantizers();
6486 color = make_combined_color_sampler(mv->mesh().its, mv->texture_displacement_layers, facets, quantize,
6487 quantize_pure);
6488 }
6489 its = subdivide_mesh_adaptive(mv->mesh().its, region, m_subdivide_target_mm,
6490 int(mv->mesh().its.indices.size()) + m_subdivide_budget_k * 1000,
6491 nullptr, sampler, tol, floor, m_subdivide_border_mm, nullptr, color,
6493 } else {
6494 if (m_subdivide_count < 1)
6495 return;
6496 its = subdivide_mesh_uniform(mv->mesh().its, 0.f, m_subdivide_count);
6497 }
6498 if (its.indices.empty())
6499 return;
6500 m_subdivide_preview_tris = int(its.indices.size());
6501
6502 GLModel::Geometry init_data;
6504 init_data.reserve_vertices(its.vertices.size());
6505 init_data.reserve_indices(its.indices.size() * 6);
6506 for (const Vec3f &v : its.vertices)
6507 init_data.add_vertex(v);
6508 for (const stl_triangle_vertex_indices &tri : its.indices)
6509 for (int i = 0; i < 3; ++i)
6510 init_data.add_line(unsigned(tri[i]), unsigned(tri[(i + 1) % 3]));
6511 if (!init_data.is_empty())
6512 m_subdivide_preview_glmodel.init_from(std::move(init_data));
6513}
std::pair< ColorQuantizeFn, ColorQuantizeFn > palette_quantizers()
Definition GLGizmoTextureDisplacement.cpp:5779
indexed_triangle_set subdivide_mesh_uniform(const indexed_triangle_set &mesh, float max_edge_length_mm, int max_iterations)
Definition TextureDisplacement.cpp:3907

References Slic3r::GUI::GLModel::Geometry::add_line(), Slic3r::GUI::GLModel::Geometry::add_vertex(), any_layer_colors(), collect_paint_region(), facets_data_of(), Slic3r::GUI::GLModel::Geometry::format, Slic3r::GUI::GLModel::Geometry::is_empty(), Slic3r::TriangleMesh::its, Slic3r::GUI::GLModel::Geometry::Lines, m_subdivide_adaptive, m_subdivide_border_mm, m_subdivide_budget_k, m_subdivide_color_mm, m_subdivide_count, m_subdivide_detail_mm, m_subdivide_feature, m_subdivide_min_edge_mm, m_subdivide_preview_glmodel, m_subdivide_preview_tris, m_subdivide_target_mm, Slic3r::make_combined_color_sampler(), Slic3r::make_combined_displacement_sampler(), Slic3r::ModelVolume::mesh(), Slic3r::GUI::GLModel::Geometry::P3, palette_quantizers(), Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), Slic3r::subdivide_mesh_adaptive(), Slic3r::subdivide_mesh_uniform(), Slic3r::ModelVolume::texture_displacement_layers, and texture_volume().

Referenced by rebuild_preview().

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

◆ rebuild_uvcheck_mesh()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_uvcheck_mesh ( )
private
2147{
2148 m_uvcheck_glmodel.reset();
2150 return;
2151
2152 const ModelVolume *mv = texture_volume();
2153 if (mv == nullptr || m_triangle_selectors.empty())
2154 return;
2155 std::vector<int> patch_source; // only ModelUV reads it; see compute_layer_vertex_uvs()
2156 const indexed_triangle_set patch =
2157 m_triangle_selectors[0]->get_facets_strict(EnforcerBlockerType::ENFORCER, &patch_source);
2158 if (patch.indices.empty())
2159 return;
2160 // Everything below works in the *patch's* vertex space, not the mesh's. Those agree only until a
2161 // brush stroke splits a triangle, after which the patch array is longer - and since patch triangle
2162 // indices are used to index it, reading the mesh's array instead would run off the end. An earlier
2163 // version guarded that by bailing out, which quietly disabled the Checker and Distortion overlays
2164 // for anything painted with the brush.
2165 const TextureDisplacementLayer *layer = active_layer();
2166 if (layer == nullptr)
2167 return;
2168
2169 // The checker samples wherever the projection puts it; the projections the shader can't
2170 // reconstruct (LSCM, ViewProjected) get a precomputed per-vertex uv, the rest project in-shader.
2171 std::vector<Vec2f> uv = compute_layer_vertex_uvs(patch, *layer, &patch_source);
2172 const bool have_uvs = uv.size() == patch.vertices.size();
2173 m_uvcheck_uses_vertex_uv = have_uvs;
2174 m_uvcheck_projection_mode = have_uvs ? 0 :
2176 // Per corner as well: under LSCM a seam vertex has a
2177 // different uv in each island it borders, so the shared-vertex form drew one triangle per face from
2178 // a neighbouring island's placement. Only the *drawing* needs this; the distortion metric below is
2179 // a per-vertex average by construction and keeps using `uv`.
2180 const std::vector<Vec2f> corner_uv = compute_layer_corner_uvs(patch, *layer, &patch_source);
2181 const bool have_corner_uvs = have_uvs && corner_uv.size() == patch.indices.size() * 3;
2182
2183 // Per-vertex area distortion in [0,1] (0.5 == ideal), only when both requested and possible.
2184 std::vector<float> distortion(patch.vertices.size(), 0.5f);
2185 if (m_uv_check_mode == UVCheckMode::Distortion && have_uvs) {
2186 std::vector<float> tri_log(patch.indices.size(), 0.f);
2187 for (size_t f = 0; f < patch.indices.size(); ++f) {
2188 const stl_triangle_vertex_indices &t = patch.indices[f];
2189 const float a3 = 0.5f * (patch.vertices[t[1]] - patch.vertices[t[0]]).cross(patch.vertices[t[2]] - patch.vertices[t[0]]).norm();
2190 const Vec2f e0 = uv[t[1]] - uv[t[0]];
2191 const Vec2f e1 = uv[t[2]] - uv[t[0]];
2192 const float a2 = 0.5f * std::abs(e0.x() * e1.y() - e0.y() * e1.x());
2193 tri_log[f] = (a3 > 1e-12f && a2 > 1e-12f) ? std::log2(a2 / a3) : 0.f;
2194 }
2195 // Centre the heatmap on the patch's own median stretch, so a globally-scaled unwrap reads as
2196 // uniformly "ideal" and only *relative* stretching (the thing that matters) shows up as colour.
2197 std::vector<float> sorted = tri_log;
2198 float median = 0.f;
2199 if (!sorted.empty()) {
2200 std::nth_element(sorted.begin(), sorted.begin() + sorted.size() / 2, sorted.end());
2201 median = sorted[sorted.size() / 2];
2202 }
2203 std::vector<float> sum(patch.vertices.size(), 0.f);
2204 std::vector<int> cnt(patch.vertices.size(), 0);
2205 for (size_t f = 0; f < patch.indices.size(); ++f) {
2206 // +/- 2 stops (4x stretch either way) spans the full blue->red range.
2207 const float d = std::clamp(0.5f + (tri_log[f] - median) / 4.f, 0.f, 1.f);
2208 for (int k = 0; k < 3; ++k) {
2209 sum[patch.indices[f][k]] += d;
2210 ++cnt[patch.indices[f][k]];
2211 }
2212 }
2213 for (size_t v = 0; v < distortion.size(); ++v)
2214 if (cnt[v] > 0)
2215 distortion[v] = sum[v] / float(cnt[v]);
2216 }
2217
2218 GLModel::Geometry init_data;
2220 // Flat (one vertex per triangle corner), so each triangle can carry its own island's uv - see
2221 // have_corner_uvs above. Costs nothing in shading quality: the overlay shades from uv and the
2222 // interpolated distortion value alone, never from a per-vertex normal.
2223 init_data.reserve_vertices(patch.indices.size() * 3);
2224 init_data.reserve_indices(patch.indices.size() * 3);
2225 unsigned vcount = 0;
2226 for (size_t f = 0; f < patch.indices.size(); ++f) {
2227 const stl_triangle_vertex_indices &tri = patch.indices[f];
2228 for (int k = 0; k < 3; ++k) {
2229 const size_t vi = size_t(tri[k]);
2230 init_data.add_vertex(patch.vertices[vi], Vec3f(distortion[vi], 0.f, 0.f),
2231 have_corner_uvs ? corner_uv[f * 3 + size_t(k)] :
2232 (have_uvs ? uv[vi] : Vec2f::Zero()));
2233 }
2234 init_data.add_triangle(vcount, vcount + 1, vcount + 2);
2235 vcount += 3;
2236 }
2237
2238 m_uvcheck_glmodel.init_from(std::move(init_data));
2239}
Vec3f m_uvcheck_patch_axis
Definition GLGizmoTextureDisplacement.hpp:1059
int m_uvcheck_projection_mode
Definition GLGizmoTextureDisplacement.hpp:1057
bool m_uvcheck_uses_vertex_uv
Definition GLGizmoTextureDisplacement.hpp:1055
Vec3f m_uvcheck_patch_center
Definition GLGizmoTextureDisplacement.hpp:1058

References active_layer(), Slic3r::GUI::GLModel::Geometry::add_triangle(), Slic3r::GUI::GLModel::Geometry::add_vertex(), compute_layer_corner_uvs(), compute_layer_vertex_uvs(), Slic3r::cross(), Distortion, Slic3r::ENFORCER, Slic3r::f(), Slic3r::GUI::GLModel::Geometry::format, layer_projection_frame(), Slic3r::GUI::GLGizmoPainterBase::m_triangle_selectors, m_uv_check_mode, m_uvcheck_glmodel, m_uvcheck_patch_axis, m_uvcheck_patch_center, m_uvcheck_projection_mode, m_uvcheck_uses_vertex_uv, None, Slic3r::GUI::GLModel::Geometry::P3N3T2, Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), texture_volume(), and Slic3r::GUI::GLModel::Geometry::Triangles.

Referenced by apply_view_mode(), and rebuild_preview().

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

◆ rebuild_wireframe_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_wireframe_overlay ( )
private
2315{
2316 if (!m_wireframe_overlay) {
2319 return;
2320 }
2321 const ModelVolume *mv = texture_volume();
2322 if (mv == nullptr)
2323 return;
2324 const indexed_triangle_set &its = mv->mesh().its;
2325 if (its.indices.empty())
2326 return;
2327
2328 // Building from the base mesh (shaded/paint mode); its topology only changes on bake/subdivide, and
2329 // this runs on every rebuild_preview(), so rebuild only when the vertex count actually changes.
2330 if (m_wireframe_overlay_glmodel.is_initialized() && m_wireframe_overlay_vcount == its.vertices.size())
2331 return;
2333}
void build_wireframe_from_its(const indexed_triangle_set &its)
Definition GLGizmoTextureDisplacement.cpp:2291

References build_wireframe_from_its(), Slic3r::TriangleMesh::its, m_wireframe_overlay, m_wireframe_overlay_glmodel, m_wireframe_overlay_vcount, Slic3r::ModelVolume::mesh(), and texture_volume().

Referenced by refresh_wireframe().

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

◆ refresh_wireframe()

void Slic3r::GUI::GLGizmoTextureDisplacement::refresh_wireframe ( )
private
2336{
2337 if (!m_wireframe_overlay) {
2340 return;
2341 }
2342 // The wireframe has to sit on whatever mesh is actually on screen. In the true-displacement view
2343 // that is the raised preview geometry (m_preview_its) - drawing the flat base mesh's edges there
2344 // leaves them buried inside the relief, which is why the wireframe "didn't show in real mode". In
2345 // Fast (shaded) mode or with nothing painted, the surface is the undisplaced base mesh.
2346 if (!m_use_shaded_preview && !m_preview_its.indices.empty())
2348 else
2350}
void rebuild_wireframe_overlay()
Definition GLGizmoTextureDisplacement.cpp:2314

References build_wireframe_from_its(), m_preview_its, m_use_shaded_preview, m_wireframe_overlay, m_wireframe_overlay_glmodel, m_wireframe_overlay_vcount, and rebuild_wireframe_overlay().

Referenced by apply_view_mode(), queue_preview_job(), rebuild_preview(), and show_debug_stage().

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

◆ register_grabbers_for_picking()

void Slic3r::GUI::GLGizmoBase::register_grabbers_for_picking ( )
protectedinherited
399{
400 for (size_t i = 0; i < m_grabbers.size(); ++i) {
401 m_grabbers[i].register_raycasters_for_picking((m_group_id >= 0) ? m_group_id : i);
402 }
403}
int m_group_id
Definition GLGizmoBase.hpp:145

References m_grabbers, and m_group_id.

Referenced by register_raycasters_for_picking().

Here is the caller graph for this function:

◆ register_raycasters_for_picking()

void Slic3r::GUI::GLGizmoBase::register_raycasters_for_picking ( )
inlineinherited
void register_grabbers_for_picking()
Definition GLGizmoBase.cpp:398
virtual void on_register_raycasters_for_picking()
Definition GLGizmoBase.hpp:267

References on_register_raycasters_for_picking(), and register_grabbers_for_picking().

Referenced by Slic3r::GUI::GLGizmosManager::activate_gizmo(), and Slic3r::GUI::GLCanvas3D::reload_scene().

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

◆ remesh_model()

void Slic3r::GUI::GLGizmoTextureDisplacement::remesh_model ( )
private
6442{
6443 if (texture_volume() == nullptr || m_remesh_target_edge_mm <= 0.f)
6444 return;
6445
6446 update_model_object(); // the paint rides across the remesh, so flush any in-progress stroke first
6447
6448 TextureDisplacementPrepareParams params;
6449 params.remesh_edge_mm = m_remesh_target_edge_mm;
6450 params.remesh_sharp_deg = m_remesh_keep_sharp_edges ? m_remesh_sharp_angle_deg : 0.f;
6451 queue_prepare(params, _u8L("Remesh model for texture displacement"), /* then_bake */ false,
6452 _u8L("Remeshing did not change the model. It may be non-manifold (open edges or edges "
6453 "shared by more than two triangles), or the target edge length may already be met."),
6455}

References _u8L, AllParts, m_remesh_keep_sharp_edges, m_remesh_sharp_angle_deg, m_remesh_target_edge_mm, queue_prepare(), Slic3r::TextureDisplacementPrepareParams::remesh_edge_mm, Slic3r::TextureDisplacementPrepareParams::remesh_sharp_deg, texture_volume(), and update_model_object().

Here is the call graph for this function:

◆ remove_texture_layer()

void Slic3r::GUI::GLGizmoTextureDisplacement::remove_texture_layer ( int slot)
private
5148{
5149 ModelVolume *mv = texture_volume();
5150 if (!mv)
5151 return;
5152
5153 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Remove texture displacement layer"), UndoRedo::SnapshotType::GizmoAction);
5154
5155 auto &layers = mv->texture_displacement_layers;
5156 layers.erase(std::remove_if(layers.begin(), layers.end(),
5157 [slot](const TextureDisplacementLayer &l) { return l.slot == slot; }),
5158 layers.end());
5159 if (slot >= 0 && slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS)) {
5160 // Every model part: paint left in the slot on another part would still bake, and the next layer
5161 // added reuses the slot.
5162 for (ModelVolume *part : texture_parts())
5163 part->texture_displacement_facet(slot).reset();
5164 m_thumbnails[size_t(slot)].reset();
5165 m_thumbnail_source[size_t(slot)].reset();
5166 m_layer_expanded[size_t(slot)] = false;
5167 }
5168 if (m_picker_slot == slot)
5169 m_picker_slot = -1;
5170
5171 if (m_active_layer_slot == slot)
5172 update_from_model_object(false); // also rebuilds the preview
5173 else
5174 rebuild_preview(); // a non-active layer's contribution to the combined preview changed
5175
5176 m_parent.set_as_dirty();
5177}

References _u8L, Slic3r::UndoRedo::GizmoAction, m_active_layer_slot, m_layer_expanded, Slic3r::GUI::GLGizmoBase::m_parent, m_picker_slot, m_thumbnail_source, m_thumbnails, Slic3r::GUI::plater(), rebuild_preview(), Slic3r::ModelVolume::texture_displacement_layers, Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS, texture_parts(), texture_volume(), and update_from_model_object().

Here is the call graph for this function:

◆ render()

void Slic3r::GUI::GLGizmoBase::render ( )
inlineinherited
214{ on_render(); }
virtual void on_render()=0

References on_render().

Here is the call graph for this function:

◆ render_adjust_texture_gizmo()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_adjust_texture_gizmo ( )
private
3555{
3557 return;
3558
3559 const ModelObject *mo = m_c->selection_info()->model_object();
3560 const ModelVolume *mv = texture_volume();
3561 const TextureDisplacementLayer *layer = active_layer();
3562 if (mo == nullptr || mv == nullptr || layer == nullptr)
3563 return;
3564
3565 const Selection &selection = m_parent.get_selection();
3566 const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
3567
3568 // Handle sizes scale with the volume so they stay usable on both tiny and huge models.
3569 const float bbox_size = float(mv->mesh().bounding_box().size().norm());
3570 const float panel_half = bbox_size * 0.04f + 1.f;
3571 const float arrow_length = panel_half * 2.2f;
3572
3573 // Tracks the layer's offset, so the handle actually travels with the texture as it is dragged.
3574 const Vec3f handle_center_local = adjust_handle_center(*layer);
3575
3576 GLShaderProgram *shader = wxGetApp().get_shader("flat");
3577 if (shader == nullptr)
3578 return;
3579
3582 shader->start_using();
3583
3584 const Camera &camera = wxGetApp().plater()->get_camera();
3585
3586 Vec3f u_axis, v_axis;
3587 adjust_tangent_basis(u_axis, v_axis);
3588 Transform3d plane_transform = Transform3d::Identity();
3589 plane_transform.linear().col(0) = u_axis.cast<double>();
3590 plane_transform.linear().col(1) = v_axis.cast<double>();
3591 plane_transform.linear().col(2) = m_adjust_anchor_normal.cast<double>();
3592 plane_transform.translation() = handle_center_local.cast<double>();
3593
3594 // Pan panel: a flat square lying in the patch's own tangent plane. Dragging anywhere on it
3595 // moves the texture freely along both axes at once.
3596 if (!m_adjust_panel_glmodel.is_initialized()) {
3597 GLModel::Geometry init_data;
3599 init_data.reserve_vertices(4);
3600 init_data.reserve_indices(6);
3601 init_data.add_vertex(Vec3f(-1.f, -1.f, 0.f));
3602 init_data.add_vertex(Vec3f(1.f, -1.f, 0.f));
3603 init_data.add_vertex(Vec3f(1.f, 1.f, 0.f));
3604 init_data.add_vertex(Vec3f(-1.f, 1.f, 0.f));
3605 init_data.add_triangle(0, 1, 2);
3606 init_data.add_triangle(0, 2, 3);
3607 m_adjust_panel_glmodel.init_from(std::move(init_data));
3608 }
3609 Transform3d view_model_matrix = camera.get_view_matrix() * trafo_matrix * plane_transform *
3610 Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(panel_half, panel_half, panel_half));
3611 shader->set_uniform("view_model_matrix", view_model_matrix);
3612 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
3613 ColorRGBA panel_color = m_adjust_drag_handle == AdjustHandle::Pan ? ColorRGBA::YELLOW() : ColorRGBA::ORANGE();
3614 panel_color.a(0.45f);
3615 m_adjust_panel_glmodel.set_color(panel_color);
3616 m_adjust_panel_glmodel.render();
3617
3618 // Axis arrows: a shaft plus a small V-shaped arrowhead, both along local +X. Reused for both
3619 // the U and V axes below by swapping which world direction local +X is transformed to.
3620 if (!m_adjust_arrow_glmodel.is_initialized()) {
3621 GLModel::Geometry init_data;
3622 init_data.format = { GLModel::Geometry::EPrimitiveType::Lines, GLModel::Geometry::EVertexLayout::P3 };
3623 init_data.reserve_vertices(4);
3624 init_data.reserve_indices(6);
3625 init_data.add_vertex(Vec3f(0.f, 0.f, 0.f));
3626 init_data.add_vertex(Vec3f(1.f, 0.f, 0.f));
3627 init_data.add_vertex(Vec3f(0.82f, 0.08f, 0.f));
3628 init_data.add_vertex(Vec3f(0.82f, -0.08f, 0.f));
3629 init_data.add_line(0, 1);
3630 init_data.add_line(1, 2);
3631 init_data.add_line(1, 3);
3632 m_adjust_arrow_glmodel.init_from(std::move(init_data));
3633 }
3634#if !SLIC3R_OPENGL_ES
3635 if (!OpenGLManager::get_gl_info().is_core_profile())
3636 glsafe(::glLineWidth(2.0f));
3637#endif // !SLIC3R_OPENGL_ES
3638
3639 auto render_arrow = [&](const Vec3f &axis, const Vec3f &other_axis, bool is_active) {
3640 Transform3d arrow_transform = Transform3d::Identity();
3641 arrow_transform.linear().col(0) = axis.cast<double>();
3642 arrow_transform.linear().col(1) = other_axis.cast<double>();
3643 arrow_transform.linear().col(2) = m_adjust_anchor_normal.cast<double>();
3644 arrow_transform.translation() = handle_center_local.cast<double>();
3645
3646 const Transform3d vmm = camera.get_view_matrix() * trafo_matrix * arrow_transform *
3647 Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), Vec3d(arrow_length, arrow_length, arrow_length));
3648 shader->set_uniform("view_model_matrix", vmm);
3649 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
3650 m_adjust_arrow_glmodel.set_color(is_active ? ColorRGBA::YELLOW() : ColorRGBA::ORANGE());
3651 m_adjust_arrow_glmodel.render();
3652 };
3653 render_arrow(u_axis, v_axis, m_adjust_drag_handle == AdjustHandle::AxisU);
3654 render_arrow(v_axis, u_axis, m_adjust_drag_handle == AdjustHandle::AxisV);
3655
3656 shader->stop_using();
3659}
#define glsafe(cmd)
Definition OpenGLUtils.hpp:25
static const ColorRGBA YELLOW()
Definition Color.hpp:141
static const ColorRGBA ORANGE()
Definition Color.hpp:138
GLModel m_adjust_panel_glmodel
Definition GLGizmoTextureDisplacement.hpp:1225
GLModel m_adjust_arrow_glmodel
Definition GLGizmoTextureDisplacement.hpp:1226
static const GLInfo & get_gl_info()
Definition OpenGLManager.hpp:106
#define glEnable
Definition gl.h:3667
#define GL_DEPTH_TEST
Definition gl.h:574
#define GL_BLEND
Definition gl.h:275
#define glLineWidth
Definition gl.h:4215
#define glDisable
Definition gl.h:3607
void assemble_transform(Transform3d &transform, const Vec3d &translation, const Vec3d &rotation, const Vec3d &scale, const Vec3d &mirror)
Definition Geometry.cpp:319

References Slic3r::ColorRGBA::a(), active_layer(), Slic3r::GUI::GLModel::Geometry::add_line(), Slic3r::GUI::GLModel::Geometry::add_triangle(), Slic3r::GUI::GLModel::Geometry::add_vertex(), adjust_handle_center(), adjust_tangent_basis(), Slic3r::Geometry::assemble_transform(), AxisU, AxisV, Slic3r::TriangleMesh::bounding_box(), Slic3r::GUI::GLModel::Geometry::format, Slic3r::GUI::OpenGLManager::get_gl_info(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::Camera::get_view_matrix(), GL_BLEND, GL_DEPTH_TEST, glDisable, glEnable, glLineWidth, glsafe, Slic3r::ModelObject::instances, Slic3r::GUI::GLModel::Geometry::Lines, m_adjust_anchor_normal, m_adjust_anchor_valid, m_adjust_arrow_glmodel, m_adjust_drag_handle, m_adjust_panel_glmodel, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::ModelVolume::mesh(), Slic3r::ColorRGBA::ORANGE(), Slic3r::GUI::GLModel::Geometry::P3, Pan, Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), Slic3r::GLShaderProgram::set_uniform(), Slic3r::BoundingBox3Base< PointType >::size(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), texture_volume(), Slic3r::GUI::GLModel::Geometry::Triangles, and Slic3r::ColorRGBA::YELLOW().

Referenced by render_painter_gizmo().

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

◆ render_combo()

bool Slic3r::GUI::GLGizmoBase::render_combo ( const std::string & label,
const std::vector< std::string > & lines,
int & selection_idx,
float label_width,
float item_width )
protectedinherited
227{
229 ImGui::AlignTextToFramePadding();
230 m_imgui->text(label);
231 ImGui::SameLine(label_width);
232 ImGui::PushItemWidth(item_width);
233
234 size_t selection_out = selection_idx;
235
236 const char *selected_str = (selection_idx >= 0 && selection_idx < int(lines.size())) ? lines[selection_idx].c_str() : "";
237 if (ImGui::BBLBeginCombo(("##" + label).c_str(), selected_str, 0)) {
238 for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
239 ImGui::PushID(int(line_idx));
240 if (ImGui::Selectable("", line_idx == selection_idx)) selection_out = line_idx;
241
242 ImGui::SameLine();
243 ImGui::Text("%s", lines[line_idx].c_str());
244 ImGui::PopID();
245 }
246
247 ImGui::EndCombo();
248 }
249
250 bool is_changed = selection_idx != selection_out;
251 selection_idx = selection_out;
252
253 //if (is_changed) update_connector_shape();
255
256 return is_changed;
257}

References m_imgui, m_parent, Slic3r::GUI::ImGuiWrapper::pop_combo_style(), and Slic3r::GUI::ImGuiWrapper::push_combo_style().

Referenced by Slic3r::GUI::GLGizmoAssembly::render_assembly_mode_combo(), and Slic3r::GUI::GLGizmoCut3D::render_connectors_input_window().

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

◆ render_cross_mark()

void Slic3r::GUI::GLGizmoBase::render_cross_mark ( const Vec3f & target,
bool is_single = false )
protectedinherited
260{
261 const float half_length = 4.0f;
262
263 // ORCA: OpenGL Core Profile
264#if !SLIC3R_OPENGL_ES
265 if (!OpenGLManager::get_gl_info().is_core_profile())
266 glsafe(::glLineWidth(2.0f));
267#endif // !SLIC3R_OPENGL_ES
268
269 auto render_line = [](const Vec3f& p1, const Vec3f& p2, const ColorRGBA& color) {
270 GLModel::Geometry init_data;
272 init_data.color = color;
273 init_data.reserve_vertices(2);
274 init_data.reserve_indices(2);
275
276 // vertices
277 init_data.add_vertex(p1);
278 init_data.add_vertex(p2);
279
280 // indices
281 init_data.add_line(0, 1);
282
283 GLModel model;
284 model.init_from(std::move(init_data));
285 model.render();
286 };
287
288 // draw line for x axis
289 if (!is_single) {
290 render_line(
291 {target(0) - half_length, target(1), target(2)},
292 {target(0) + half_length, target(1), target(2)},
293 ColorRGBA::X()); // ORCA match axis colors
294 }
295 else {
296 render_line(
297 {target(0), target(1), target(2)},
298 {target(0) + half_length, target(1), target(2)},
299 ColorRGBA::X()); // ORCA match axis colors
300 }
301 // draw line for y axis
302 if (!is_single) {
303 render_line(
304 {target(0), target(1) - half_length, target(2)},
305 {target(0), target(1) + half_length, target(2)},
306 ColorRGBA::Y()); // ORCA match axis colors
307 } else {
308 render_line(
309 {target(0), target(1), target(2)},
310 {target(0), target(1) + half_length, target(2)},
311 ColorRGBA::Y()); // ORCA match axis colors
312 }
313 // draw line for z axis
314 if (!is_single) {
315 render_line(
316 {target(0), target(1), target(2) - half_length},
317 {target(0), target(1), target(2) + half_length},
318 ColorRGBA::Z()); // ORCA match axis colors
319 } else {
320 render_line(
321 {target(0), target(1), target(2)},
322 {target(0), target(1), target(2) + half_length},
323 ColorRGBA::Z()); // ORCA match axis colors
324 }
325}
static const ColorRGBA Y()
Definition Color.hpp:146
static const ColorRGBA X()
Definition Color.hpp:145
static const ColorRGBA Z()
Definition Color.hpp:147

References Slic3r::GUI::GLModel::Geometry::add_line(), Slic3r::GUI::GLModel::Geometry::add_vertex(), Slic3r::GUI::GLModel::Geometry::color, Slic3r::GUI::GLModel::Geometry::format, Slic3r::GUI::OpenGLManager::get_gl_info(), glLineWidth, glsafe, Slic3r::GUI::GLModel::init_from(), Slic3r::GUI::GLModel::Geometry::Lines, Slic3r::GUI::GLModel::Geometry::P3, Slic3r::GUI::GLModel::render(), Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), Slic3r::ColorRGBA::X(), Slic3r::ColorRGBA::Y(), and Slic3r::ColorRGBA::Z().

Referenced by Slic3r::GUI::GLGizmoMove3D::on_render().

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

◆ render_cursor()

void Slic3r::GUI::GLGizmoPainterBase::render_cursor ( )
protectedinherited
219{
220 // First check that the mouse pointer is on an object.
221 const Camera& camera = wxGetApp().plater()->get_camera();
222
223 // Precalculate transformations of individual meshes.
224 const std::vector<Transform3d> trafo_matrices = mesh_trafo_matrices();
225 // Raycast and return if there's no hit.
226 update_raycast_cache(m_parent.get_local_mouse_position(), camera, trafo_matrices);
227 if (m_rr.mesh_id == -1)
228 return;
229
232 render_cursor_sphere(trafo_matrices[m_rr.mesh_id]);
236 render_cursor_height_range(trafo_matrices[m_rr.mesh_id]);
237 }
238}
void render_cursor_circle()
Definition GLGizmoPainterBase.cpp:240
std::vector< Transform3d > mesh_trafo_matrices() const
Definition GLGizmoPainterBase.cpp:185
void render_cursor_sphere(const Transform3d &trafo) const
Definition GLGizmoPainterBase.cpp:374
void render_cursor_height_range(const Transform3d &trafo) const
Definition GLGizmoPainterBase.cpp:419

References BRUSH, Slic3r::TriangleSelector::CIRCLE, Slic3r::TriangleSelector::HEIGHT_RANGE, m_cursor_type, Slic3r::GUI::GLGizmoBase::m_parent, m_rr, m_tool_type, mesh_trafo_matrices(), render_cursor_circle(), render_cursor_height_range(), render_cursor_sphere(), Slic3r::TriangleSelector::SPHERE, and update_raycast_cache().

Referenced by Slic3r::GUI::GLGizmoFdmSupports::render_painter_gizmo(), Slic3r::GUI::GLGizmoFuzzySkin::render_painter_gizmo(), Slic3r::GUI::GLGizmoMmuSegmentation::render_painter_gizmo(), Slic3r::GUI::GLGizmoSeam::render_painter_gizmo(), and Slic3r::GUI::GLGizmoTextureDisplacement::render_painter_gizmo().

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

◆ render_cursor_circle()

void Slic3r::GUI::GLGizmoPainterBase::render_cursor_circle ( )
protectedinherited
241{
242 const Size cnv_size = m_parent.get_canvas_size();
243 const float cnv_width = float(cnv_size.get_width());
244 const float cnv_height = float(cnv_size.get_height());
245 if (cnv_width == 0.0f || cnv_height == 0.0f)
246 return;
247
248 const float cnv_inv_width = 1.0f / cnv_width;
249 const float cnv_inv_height = 1.0f / cnv_height;
250
251 const Vec2d center = m_parent.get_local_mouse_position();
252 const float zoom = float(wxGetApp().plater()->get_camera().get_zoom());
253 const float radius = m_cursor_radius * float(wxGetApp().plater()->get_camera().get_zoom());
254
255#if !SLIC3R_OPENGL_ES
256 if (!OpenGLManager::get_gl_info().is_core_profile())
257 glsafe(::glLineWidth(1.5f));
258#endif // !SLIC3R_OPENGL_ES
259
261
262#if !SLIC3R_OPENGL_ES
263 if (!OpenGLManager::get_gl_info().is_core_profile()) {
265 glsafe(::glLineStipple(4, 0xAAAA));
267 }
268#endif // !SLIC3R_OPENGL_ES
269
270 if (!m_circle.is_initialized() || !m_old_center.isApprox(center) || std::abs(m_old_cursor_radius - radius) > EPSILON) {
271 m_old_cursor_radius = radius;
273 m_circle.reset();
274
275 GLModel::Geometry init_data;
276 unsigned int steps_count = 0;
277#if !SLIC3R_OPENGL_ES
278 if (OpenGLManager::get_gl_info().is_core_profile()) {
279#endif // !SLIC3R_OPENGL_ES
280 steps_count = (unsigned int)(2 * (4 + int(252 * (zoom - 1.0f) / (250.0f - 1.0f))));
282#if !SLIC3R_OPENGL_ES
283 }
284 else {
285 steps_count = 32;
287 }
288#endif // !SLIC3R_OPENGL_ES
289 const float step_size = 2.0f * float(PI) / float(steps_count);
290 init_data.color = { 0.0f, 1.0f, 0.3f, 1.0f };
291 init_data.reserve_vertices(steps_count);
292 init_data.reserve_indices(steps_count);
293
294 // vertices + indices
295 for (unsigned int i = 0; i < steps_count; ++i) {
296#if !SLIC3R_OPENGL_ES
297 if (OpenGLManager::get_gl_info().is_core_profile()) {
298#endif // !SLIC3R_OPENGL_ES
299 if (i % 2 != 0) continue;
300
301 const float angle_i = float(i) * step_size;
302 const unsigned int j = (i + 1) % steps_count;
303 const float angle_j = float(j) * step_size;
304 const Vec2d v_i(::cos(angle_i), ::sin(angle_i));
305 const Vec2d v_j(::cos(angle_j), ::sin(angle_j));
306 init_data.add_vertex(Vec2f(v_i.x(), v_i.y()));
307 init_data.add_vertex(Vec2f(v_j.x(), v_j.y()));
308 const size_t vcount = init_data.vertices_count();
309 init_data.add_line(vcount - 2, vcount - 1);
310#if !SLIC3R_OPENGL_ES
311 }
312 else {
313 const float angle = float(i) * step_size;
314 init_data.add_vertex(Vec2f(2.0f * ((center.x() + ::cos(angle) * radius) * cnv_inv_width - 0.5f),
315 -2.0f * ((center.y() + ::sin(angle) * radius) * cnv_inv_height - 0.5f)));
316 init_data.add_index(i);
317 }
318#endif // !SLIC3R_OPENGL_ES
319 }
320
321 m_circle.init_from(std::move(init_data));
322 }
323
324 // BBS
325 ColorRGBA render_color = this->get_cursor_hover_color();
327 render_color = this->get_cursor_sphere_left_button_color();
328 else if (m_button_down == Button::Right)
329 render_color = this->get_cursor_sphere_right_button_color();
330
331 m_circle.set_color(render_color);
332
333#if SLIC3R_OPENGL_ES
334 GLShaderProgram* shader = wxGetApp().get_shader("dashed_lines");
335#else
336 GLShaderProgram* shader = OpenGLManager::get_gl_info().is_core_profile() ? wxGetApp().get_shader("dashed_thick_lines") : wxGetApp().get_shader("flat");
337#endif // SLIC3R_OPENGL_ES
338 if (shader != nullptr) {
339 shader->start_using();
340#if !SLIC3R_OPENGL_ES
341 if (OpenGLManager::get_gl_info().is_core_profile()) {
342#endif // !SLIC3R_OPENGL_ES
343 const Transform3d view_model_matrix = Geometry::translation_transform(Vec3d(2.0f * (center.x() * cnv_inv_width - 0.5f), -2.0f * (center.y() * cnv_inv_height - 0.5f), 0.0)) *
344 Geometry::scale_transform(Vec3d(2.0f * radius * cnv_inv_width, 2.0f * radius * cnv_inv_height, 1.0f));
345 shader->set_uniform("view_model_matrix", view_model_matrix);
346#if !SLIC3R_OPENGL_ES
347 }
348 else
349 shader->set_uniform("view_model_matrix", Transform3d::Identity());
350#endif // !SLIC3R_OPENGL_ES
351 shader->set_uniform("projection_matrix", Transform3d::Identity());
352#if !SLIC3R_OPENGL_ES
353 if (OpenGLManager::get_gl_info().is_core_profile()) {
354#endif // !SLIC3R_OPENGL_ES
355 const std::array<int, 4>& viewport = wxGetApp().plater()->get_camera().get_viewport();
356 shader->set_uniform("viewport_size", Vec2d(double(viewport[2]), double(viewport[3])));
357 shader->set_uniform("width", 0.25f);
358 shader->set_uniform("gap_size", 0.0f);
359#if !SLIC3R_OPENGL_ES
360 }
361#endif // !SLIC3R_OPENGL_ES
362 m_circle.render();
363 shader->stop_using();
364 }
365
366#if !SLIC3R_OPENGL_ES
367 if (!OpenGLManager::get_gl_info().is_core_profile())
369#endif // !SLIC3R_OPENGL_ES
371}
Vec2d m_old_center
Definition GLGizmoPainterBase.hpp:308
virtual ColorRGBA get_cursor_hover_color() const
Definition GLGizmoPainterBase.hpp:254
virtual ColorRGBA get_cursor_sphere_right_button_color() const
Definition GLGizmoPainterBase.hpp:252
virtual ColorRGBA get_cursor_sphere_left_button_color() const
Definition GLGizmoPainterBase.hpp:251
GLModel m_circle
Definition GLGizmoPainterBase.hpp:307
float m_old_cursor_radius
Definition GLGizmoPainterBase.hpp:309
bool is_core_profile() const
Definition OpenGLManager.hpp:53
#define GL_LINE_STIPPLE
Definition gl.h:941
#define GL_ENABLE_BIT
Definition gl.h:639
#define glLineStipple
Definition gl.h:4213
#define glPopAttrib
Definition gl.h:4477
#define glPushAttrib
Definition gl.h:4597
static constexpr double PI
Definition libslic3r.h:81
void translation_transform(Transform3d &transform, const Vec3d &translation)
Definition Geometry.cpp:346
void scale_transform(Transform3d &transform, double scale)
Definition Geometry.cpp:372
double angle(const Eigen::MatrixBase< Derived > &v1, const Eigen::MatrixBase< Derived2 > &v2)
Definition Point.hpp:138

References Slic3r::GUI::GLModel::Geometry::add_index(), Slic3r::GUI::GLModel::Geometry::add_line(), Slic3r::GUI::GLModel::Geometry::add_vertex(), Slic3r::angle(), Slic3r::center(), Slic3r::GUI::GLModel::Geometry::color, EPSILON, Slic3r::GUI::GLModel::Geometry::format, get_cursor_hover_color(), get_cursor_sphere_left_button_color(), get_cursor_sphere_right_button_color(), Slic3r::GUI::OpenGLManager::get_gl_info(), Slic3r::GUI::Size::get_height(), Slic3r::GUI::Size::get_width(), GL_DEPTH_TEST, GL_ENABLE_BIT, GL_LINE_STIPPLE, glDisable, glEnable, glLineStipple, glLineWidth, glPopAttrib, glPushAttrib, glsafe, Slic3r::GUI::OpenGLManager::GLInfo::is_core_profile(), Left, Slic3r::GUI::GLModel::Geometry::LineLoop, Slic3r::GUI::GLModel::Geometry::Lines, m_button_down, m_circle, m_cursor_radius, m_old_center, m_old_cursor_radius, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::GUI::GLModel::Geometry::P2, PI, Slic3r::GUI::plater(), Slic3r::GUI::GLModel::Geometry::reserve_indices(), Slic3r::GUI::GLModel::Geometry::reserve_vertices(), Right, Slic3r::Geometry::scale_transform(), Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), Slic3r::Geometry::translation_transform(), and Slic3r::GUI::GLModel::Geometry::vertices_count().

Referenced by render_cursor().

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

◆ render_cursor_height_range()

void Slic3r::GUI::GLGizmoPainterBase::render_cursor_height_range ( const Transform3d & trafo) const
protectedinherited
420{
421 GLShaderProgram *shader = wxGetApp().get_shader("flat");
422 if (shader == nullptr)
423 return;
424
425 shader->start_using();
426
427 const BoundingBoxf3 box = bounding_box();
428 Vec3d hit_world = trafo * Vec3d(m_rr.hit(0), m_rr.hit(1), m_rr.hit(2));
429 float max_z = (float)box.max.z();
430 float min_z = (float)box.min.z();
431
432 float cursor_z = std::clamp((float)hit_world.z(), min_z, max_z);
433 std::array<float, 2> zs = { cursor_z, std::clamp(cursor_z + m_cursor_height, min_z, max_z) };
434
435 const Selection& selection = m_parent.get_selection();
436 const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()];
437 const ModelInstance* mi = model_object->instances[selection.get_instance_idx()];
438
439 int volumes_count = model_object->volumes.size();
440 if (m_cut_contours.size() != volumes_count * 2) {
441 m_cut_contours.resize(volumes_count * 2);
442 }
443 m_volumes_index = 0;
444 for (const ModelVolume* mv : model_object->volumes) {
445 TriangleMesh vol_mesh = mv->mesh();
446 if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
447 Transform3d temp = mi->get_assemble_transformation().get_matrix() * mv->get_matrix();
448 temp.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mi->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
449 vol_mesh.transform(temp);
450 }
451 else {
452 vol_mesh.transform(mi->get_transformation().get_matrix() * mv->get_matrix());
453 }
454
455 for (int i = 0; i < zs.size(); i++) {
456 update_contours(m_volumes_index, vol_mesh, zs[i], max_z, min_z);
457
458 const Camera& camera = wxGetApp().plater()->get_camera();
459 Transform3d view_model_matrix = camera.get_view_matrix() * Geometry::assemble_transform(m_cut_contours[m_volumes_index].shift);
460
461 shader->set_uniform("view_model_matrix", view_model_matrix);
462 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
463 // ORCA: OpenGL Core Profile
464#if !SLIC3R_OPENGL_ES
465 if (!OpenGLManager::get_gl_info().is_core_profile())
466 glsafe(::glLineWidth(2.0f));
467#endif // !SLIC3R_OPENGL_ES
468 m_cut_contours[m_volumes_index].contours.render();
470 }
471
472 }
473
474 shader->stop_using();
475}
int m_volumes_index
Definition GLGizmoPainterBase.hpp:385
void update_contours(int i, const TriangleMesh &vol_mesh, float cursor_z, float max_z, float min_z) const
Definition GLGizmoPainterBase.cpp:490
BoundingBoxf3 bounding_box() const
Definition GLGizmoPainterBase.cpp:477
std::vector< CutContours > m_cut_contours
Definition GLGizmoPainterBase.hpp:384

References Slic3r::Geometry::assemble_transform(), bounding_box(), Slic3r::GUI::GLCanvas3D::CanvasAssembleView, Slic3r::GLVolume::explosion_ratio, Slic3r::ModelInstance::get_assemble_transformation(), Slic3r::GUI::OpenGLManager::get_gl_info(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::Geometry::Transformation::get_matrix(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Selection::get_object_idx(), Slic3r::Geometry::Transformation::get_offset(), Slic3r::ModelInstance::get_offset_to_assembly(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::ModelInstance::get_transformation(), Slic3r::ModelVolume::get_transformation(), Slic3r::GUI::Camera::get_view_matrix(), glLineWidth, glsafe, Slic3r::ModelObject::instances, m_cursor_height, m_cut_contours, Slic3r::GUI::GLGizmoBase::m_parent, m_rr, m_volumes_index, Slic3r::BoundingBoxBase< PointType, std::vector< PointType > >::max, Slic3r::ModelVolume::mesh(), Slic3r::BoundingBoxBase< PointType, std::vector< PointType > >::min, Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), Slic3r::TriangleMesh::transform(), update_contours(), and Slic3r::ModelObject::volumes.

Referenced by render_cursor().

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

◆ render_cursor_sphere()

void Slic3r::GUI::GLGizmoPainterBase::render_cursor_sphere ( const Transform3d & trafo) const
protectedinherited
375{
376 if (s_sphere == nullptr) {
377 s_sphere = std::make_shared<GLModel>();
378 s_sphere->init_from(its_make_sphere(1.0, double(PI) / 12.0));
379 }
380
381 GLShaderProgram* shader = wxGetApp().get_shader("flat");
382 if (shader == nullptr)
383 return;
384
385 const Transform3d complete_scaling_matrix_inverse = Geometry::Transformation(trafo).get_scaling_factor_matrix().inverse();
386
387 // BBS
388 ColorRGBA render_color = this->get_cursor_hover_color();
390 render_color = this->get_cursor_sphere_left_button_color();
391 else if (m_button_down == Button::Right)
392 render_color = this->get_cursor_sphere_right_button_color();
393
394 shader->start_using();
395
396 const Camera& camera = wxGetApp().plater()->get_camera();
397 Transform3d view_model_matrix = camera.get_view_matrix() * trafo *
398 Geometry::assemble_transform(m_rr.hit.cast<double>()) * complete_scaling_matrix_inverse *
399 Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), m_cursor_radius * Vec3d::Ones());
400
401 shader->set_uniform("view_model_matrix", view_model_matrix);
402 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
403
404 const bool is_left_handed = Geometry::Transformation(view_model_matrix).is_left_handed();
405 if (is_left_handed)
407
408 assert(s_sphere != nullptr);
409 s_sphere->set_color(render_color);
410 s_sphere->render();
411
412 if (is_left_handed)
414
415 shader->stop_using();
416}
static std::shared_ptr< GLModel > s_sphere
Definition GLGizmoPainterBase.hpp:353
#define GL_CW
Definition gl.h:520
#define GL_CCW
Definition gl.h:317
#define glFrontFace
Definition gl.h:3765
static bool is_left_handed(const Transform3d::ConstLinearPart &m)
Definition Selection.cpp:2998
indexed_triangle_set its_make_sphere(double radius, double fa)
Definition TriangleMesh.cpp:1128

References Slic3r::Geometry::assemble_transform(), get_cursor_hover_color(), get_cursor_sphere_left_button_color(), get_cursor_sphere_right_button_color(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::Geometry::Transformation::get_scaling_factor_matrix(), Slic3r::GUI::Camera::get_view_matrix(), GL_CCW, GL_CW, glFrontFace, glsafe, Slic3r::Geometry::Transformation::is_left_handed(), Slic3r::GUI::is_left_handed(), Slic3r::its_make_sphere(), Left, m_button_down, m_cursor_radius, m_rr, PI, Right, s_sphere, Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), and Slic3r::GLShaderProgram::stop_using().

Referenced by render_cursor().

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

◆ render_debug_stage_panel()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_debug_stage_panel ( ModelVolume * mv)
private
6833{
6834 if (mv == nullptr)
6835 return;
6836
6837 ImGui::Separator();
6838 if (!ImGui::CollapsingHeader(_u8L("Bake stages (debug)").c_str()))
6839 return;
6840
6842 !mv->is_texture_displacement_painted());
6843 if (m_imgui->button(m_debug_in_progress ? _L("Capturing...") : _L("Capture stages")))
6845 m_imgui->disabled_end();
6846 if (ImGui::IsItemHovered())
6847 panel_tooltip(_u8L("Run the same bake the Bake button runs, keeping the mesh after every "
6848 "stage, and step through them here. Nothing is committed to the model - "
6849 "this is for finding which stage a bad result came from."));
6850
6851 ImGui::SameLine();
6852 m_imgui->bbl_checkbox(_L("Check topology"), m_debug_check_topology);
6853 if (ImGui::IsItemHovered())
6854 panel_tooltip(_u8L("Count open and non-manifold edges after each stage, which is how a stage "
6855 "that tore the mesh is spotted. It scans every edge, so it adds noticeably "
6856 "to the capture on a fine bake."));
6857
6858 if (m_debug_stages.empty())
6859 return;
6860
6861 // Stage list. Selecting one draws it; the wireframe overlay is what makes a refinement stage
6862 // readable, so it is worth leaving on while stepping.
6863 ImGui::BeginChild("##bake_stages", ImVec2(0.f, m_imgui->scaled(9.f)), true,
6864 ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize);
6865 for (size_t i = 0; i < m_debug_stages.size(); ++i) {
6866 const BakeStageSnapshot &st = m_debug_stages[i];
6867 char label[256];
6868 std::snprintf(label, sizeof(label), "%zu. %-18s %8zu tris %8.1f ms##stage%zu", i,
6869 st.name.c_str(), st.triangles, st.ms, i);
6870 if (ImGui::Selectable(label, int(i) == m_debug_stage) && !st.mesh.empty())
6871 show_debug_stage(int(i));
6872 }
6873 ImGui::EndChild();
6874
6875 // Prev / next, so stepping does not mean aiming at a list entry.
6876 m_imgui->disabled_begin(m_debug_stage <= 0);
6877 if (m_imgui->button(_L("< Previous")))
6879 m_imgui->disabled_end();
6880 ImGui::SameLine();
6881 m_imgui->disabled_begin(m_debug_stage < 0 || size_t(m_debug_stage + 1) >= m_debug_stages.size());
6882 if (m_imgui->button(_L("Next >")))
6884 m_imgui->disabled_end();
6885 ImGui::SameLine();
6886 if (m_imgui->button(_L("Close")))
6888
6889 if (m_debug_stage >= 0 && size_t(m_debug_stage) < m_debug_stages.size()) {
6890 const BakeStageSnapshot &st = m_debug_stages[size_t(m_debug_stage)];
6891 m_imgui->text(from_u8(st.name) + ": " + std::to_string(st.triangles) + " " +
6892 _u8L("triangles") + ", " + std::to_string(st.vertices) + " " + _u8L("vertices"));
6893 if (!st.detail.empty())
6894 m_imgui->text(from_u8(st.detail));
6895 if (st.topology_checked) {
6896 // Zero on both counts is the claim every stage's header makes; anything else is the bug.
6897 const std::string topo = _u8L("Open edges") + ": " + std::to_string(st.open_edges) + " " +
6898 _u8L("Non-manifold") + ": " + std::to_string(st.non_manifold_edges) +
6899 " " + _u8L("Degenerate") + ": " + std::to_string(st.degenerate);
6900 if (st.open_edges > 0 || st.non_manifold_edges > 0)
6901 m_imgui->warning_text(from_u8(topo));
6902 else
6903 m_imgui->text(topo);
6904 }
6905 }
6906
6907 double total_ms = 0.0;
6908 for (const BakeStageSnapshot &st : m_debug_stages)
6909 total_ms += st.ms;
6910 char total[128];
6911 std::snprintf(total, sizeof(total), "%s: %.1f ms", _u8L("Total").c_str(), total_ms);
6912 m_imgui->text(total);
6913
6914 if (m_imgui->button(_L("Write stage meshes"))) {
6915 const std::string dir =
6916 (boost::filesystem::temp_directory_path() / "orca-bake-stages").string();
6917 const size_t written = dump_bake_stages(m_debug_stages, dir);
6918 if (written > 0)
6919 show_info(nullptr, from_u8(_u8L("Wrote the stage meshes as OBJ files to:") + "\n" + dir));
6920 else
6921 show_error(nullptr, _u8L("Could not write the stage meshes."));
6922 }
6923 if (ImGui::IsItemHovered())
6924 panel_tooltip(_u8L("Write every captured stage out as an OBJ file, so they can be opened "
6925 "side by side in a mesh viewer."));
6926}
bool m_debug_in_progress
Definition GLGizmoTextureDisplacement.hpp:866
void run_stage_debug()
Definition GLGizmoTextureDisplacement.cpp:6739
void exit_debug_view()
Definition GLGizmoTextureDisplacement.cpp:6822
bool m_debug_check_topology
Definition GLGizmoTextureDisplacement.hpp:870
void show_debug_stage(int index)
Definition GLGizmoTextureDisplacement.cpp:6792
void show_info(wxWindow *parent, const wxString &message, const wxString &title)
Definition GUI.cpp:316
size_t dump_bake_stages(const std::vector< BakeStageSnapshot > &stages, const std::string &dir)
Definition TextureBakeDebug.cpp:164

References _L, _u8L, Slic3r::BakeStageSnapshot::degenerate, Slic3r::BakeStageSnapshot::detail, Slic3r::dump_bake_stages(), Slic3r::BakeStageMesh::empty(), exit_debug_view(), Slic3r::GUI::from_u8(), Slic3r::ModelVolume::is_texture_displacement_painted(), m_bake_in_progress, m_debug_check_topology, m_debug_in_progress, m_debug_stage, m_debug_stages, Slic3r::GUI::GLGizmoBase::m_imgui, m_prepare_in_progress, Slic3r::BakeStageSnapshot::mesh, Slic3r::BakeStageSnapshot::ms, Slic3r::BakeStageSnapshot::name, Slic3r::BakeStageSnapshot::non_manifold_edges, Slic3r::BakeStageSnapshot::open_edges, panel_tooltip(), run_stage_debug(), show_debug_stage(), Slic3r::GUI::show_error(), Slic3r::GUI::show_info(), Slic3r::BakeStageSnapshot::topology_checked, Slic3r::BakeStageSnapshot::triangles, and Slic3r::BakeStageSnapshot::vertices.

Here is the call graph for this function:

◆ render_follows_cursor()

bool Slic3r::GUI::GLGizmoPainterBase::render_follows_cursor ( ) const
overrideprotectedvirtualinherited

Reimplemented from Slic3r::GUI::GLGizmoBase.

209{
210 // The brush is drawn only where the cursor meets the model. update_raycast_cache() keeps the
211 // answer for render_cursor().
212 if (m_c->selection_info() == nullptr || m_c->selection_info()->model_object() == nullptr)
213 return false;
214 update_raycast_cache(m_parent.get_local_mouse_position(), wxGetApp().plater()->get_camera(), mesh_trafo_matrices());
215 return m_rr.mesh_id != -1;
216}

References Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, m_rr, mesh_trafo_matrices(), Slic3r::GUI::plater(), and update_raycast_cache().

Here is the call graph for this function:

◆ render_grabbers() [1/3]

void Slic3r::GUI::GLGizmoBase::render_grabbers ( const BoundingBoxf3 & box) const
protectedinherited
413{
414#if ENABLE_FIXED_GRABBER
416#else
417 render_grabbers((float)((box.size().x() + box.size().y() + box.size().z()) / 3.0));
418#endif
419}
void render_grabbers(const BoundingBoxf3 &box) const
Definition GLGizmoBase.cpp:412
static const float FixedGrabberSize
Definition GLGizmoBase.hpp:90

References Slic3r::GUI::GLGizmoBase::Grabber::FixedGrabberSize, render_grabbers(), and Slic3r::BoundingBox3Base< PointType >::size().

Referenced by Slic3r::GUI::GLGizmoMove3D::on_render(), Slic3r::GUI::GLGizmoScale3D::on_render(), Slic3r::GUI::GLGizmoRotate::render_grabber(), render_grabbers(), and render_grabbers().

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

◆ render_grabbers() [2/3]

void Slic3r::GUI::GLGizmoBase::render_grabbers ( float size) const
protectedinherited
422{
423 render_grabbers(0, m_grabbers.size() - 1, size, false);
424}

References m_grabbers, and render_grabbers().

Here is the call graph for this function:

◆ render_grabbers() [3/3]

void Slic3r::GUI::GLGizmoBase::render_grabbers ( size_t first,
size_t last,
float size,
bool force_hover ) const
protectedinherited
427{
428 GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
429 if (shader == nullptr)
430 return;
431 shader->start_using();
432 shader->set_uniform("emission_factor", 0.1f);
434 for (size_t i = first; i <= last; ++i) {
435 if (m_grabbers[i].enabled)
436 m_grabbers[i].render(force_hover ? true : m_hover_id == (int)i, size);
437 }
439 shader->stop_using();
440}
#define GL_CULL_FACE
Definition gl.h:500

References GL_CULL_FACE, glDisable, glEnable, glsafe, m_grabbers, m_hover_id, Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), and Slic3r::GLShaderProgram::stop_using().

Here is the call graph for this function:

◆ render_image_adjust_window()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_image_adjust_window ( const ImVec2 & panel_min,
const ImVec2 & panel_max )
private
4996{
4997 if (m_adjust_slot < 0)
4998 return;
4999
5000 ModelVolume *mv = texture_volume();
5001 // A different volume is selected than the one this panel was opened on. Slots are numbered per
5002 // volume, so looking m_adjust_slot up here would find an unrelated layer rather than nothing.
5003 if (mv == nullptr || mv->id().id != m_adjust_volume_id) {
5005 return;
5006 }
5007
5008 TextureDisplacementLayer *layer = nullptr;
5009 for (TextureDisplacementLayer &l : mv->texture_displacement_layers)
5010 if (l.slot == m_adjust_slot)
5011 layer = &l;
5012 if (layer == nullptr) {
5013 // The layer went away under the panel (removed, or merged away by an undo).
5015 return;
5016 }
5017
5018 // The layer's image can be swapped from the texture picker while this panel is open - the panel is a
5019 // window now, so the rest of the gizmo stays usable behind it. Re-read the source when that happens,
5020 // keeping the slider positions: the user is adjusting this layer, and the 3D view has already moved to
5021 // the new image (decode_height_texture() reads layer.image_data, which changed), so re-reading here is
5022 // also what keeps the panel's photo and the model showing the same thing.
5023 if (layer->image_data.get() != m_adjust_source_image && !load_image_adjust_source(*layer)) {
5024 close_image_adjust(); // the new image cannot be decoded; nothing left to adjust
5025 return;
5026 }
5027
5028 const float img_w = std::round(m_imgui->scaled(16.f));
5029 const float gap = m_imgui->scaled(0.5f);
5030 // Placed beside the panel on first appearance only, and left wherever the user drags it after that -
5031 // it is a window, not a popup, so its position is theirs to keep.
5032 const float win_w = img_w + 2.f * ImGui::GetStyle().WindowPadding.x;
5033 const bool left = panel_min.x - gap - win_w >= 0.f;
5034 ImGui::SetNextWindowPos(ImVec2(left ? panel_min.x - gap - win_w : panel_max.x + gap,
5035 panel_min.y + m_imgui->scaled(4.f)), ImGuiCond_Appearing);
5036
5037 bool keep_open = true;
5038 if (!ImGui::Begin((_u8L("Adjust image") + "##texture_adjust").c_str(), &keep_open,
5039 ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse)) {
5040 ImGui::End();
5041 if (!keep_open)
5043 return;
5044 }
5045
5046 // Collect a finished pass and start one for the current slider values before the image is drawn, so
5047 // the newest available result is what lands on screen this frame.
5049
5050 // The photo itself, at the image's own aspect so a non-square texture is not stretched.
5051 const float aspect = m_adjust_source.height > 0 ? float(m_adjust_source.width) / float(m_adjust_source.height) : 1.f;
5052 const float img_h = std::round(img_w / std::max(0.05f, aspect));
5054 ImGui::Image((ImTextureID) (intptr_t) m_adjust_preview_tex->get_id(), ImVec2(img_w, img_h));
5055 else
5056 ImGui::Dummy(ImVec2(img_w, img_h));
5057
5058 // The label column is sized off the widest of the five labels, so the tracks line up whatever the
5059 // translation does to their lengths.
5060 float label_w = 0.f;
5061 for (const wxString &l : { _L("Brightness"), _L("Contrast"), _L("Sharpness"), _L("Blur"), _L("Hue") })
5062 label_w = std::max(label_w, ImGui::CalcTextSize(into_u8(l).c_str()).x);
5063 label_w += m_imgui->scaled(1.2f);
5064 // An explicit track width rather than a fill-to-the-right one: this window is AlwaysAutoResize, so its
5065 // width comes from its content, and an item asking for "whatever is left" would make the width depend
5066 // on itself. The image above sets the content width, and the sliders match it.
5067 const float slider_w = std::max(m_imgui->scaled(4.f), img_w - label_w);
5068
5069 const auto row = [&](const char *id, const wxString &label, float *v, float v_min, float v_max,
5070 const char *format, bool enabled, const std::string &tip) {
5071 const float x0 = ImGui::GetCursorPosX();
5072 ImGui::AlignTextToFramePadding();
5073 m_imgui->text(label);
5074 ImGui::SameLine();
5075 ImGui::SetCursorPosX(std::max(ImGui::GetCursorPosX(), x0 + label_w));
5076 ImGui::SetNextItemWidth(slider_w);
5077 m_imgui->disabled_begin(!enabled);
5078 ImGui::SliderFloat(id, v, v_min, v_max, format, ImGuiSliderFlags_AlwaysClamp);
5079 m_imgui->disabled_end();
5080 // Only on an enabled row: a disabled item does not report hover, so a tooltip on one would never
5081 // be shown. What has to be said about a disabled slider is written out below it instead.
5082 if (enabled && !tip.empty() && ImGui::IsItemHovered())
5083 m_imgui->tooltip(tip, ImGui::GetFontSize() * 20.f);
5084 };
5085
5086 row("##adj_brightness", _L("Brightness"), &m_adjust.brightness, -1.f, 1.f, "%.2f", true, {});
5087 row("##adj_contrast", _L("Contrast"), &m_adjust.contrast, -1.f, 1.f, "%.2f", true, {});
5088 row("##adj_sharpness", _L("Sharpness"), &m_adjust.sharpness, 0.f, 1.f, "%.2f", true,
5089 _u8L("Brings out the finest detail in the image. Applied after Blur, so the two can be used "
5090 "together: blur away the speckle, then sharpen the edges that remain."));
5091 row("##adj_blur", _L("Blur"), &m_adjust.blur, 0.f, 1.f, "%.2f", true,
5092 _u8L("Blurs the image itself. The layer's own Smoothing slider softens the relief without altering "
5093 "the image, and is the one to reach for first."));
5094 // Grey has no hue to turn, and the shipped library is grayscale - so on most textures this is a
5095 // slider that would visibly do nothing. Disabled with the reason given, rather than hidden.
5096 const bool has_color = m_adjust_source.has_color();
5097 row("##adj_hue", _L("Hue"), &m_adjust.hue_deg, -180.f, 180.f, "%.0f°", has_color, {});
5098 if (!has_color) {
5099 ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + img_w);
5100 ImGui::TextDisabled("%s", _u8L("This texture is grayscale, so it has no hue to shift.").c_str());
5101 ImGui::PopTextWrapPos();
5102 }
5103
5104 if (m_imgui->button(_L("Reset")))
5105 m_adjust = TextureAdjustments{};
5106
5107 // Hand the sliders to the layer, which is what puts the adjusted image in front of the displacement:
5108 // decode_height_texture() applies preview_adjust, so the relief, the colour and the thumbnails all
5109 // follow from here. Written every frame rather than only on a change, so an undo snapshot or a
5110 // reselection that resets the layer cannot leave the model showing an adjustment the panel has moved
5111 // on from.
5112 layer->preview_adjust = m_adjust;
5113 if (m_adjust != m_adjust_applied) {
5116 // Marked dirty rather than rebuilt here: the panel's own flag is consumed by the main panel's
5117 // "rebuild once the mouse is released" rule, so dragging a slider recomputes the displacement
5118 // once, on release, instead of on every frame of the drag. That is what keeps the UI responsive
5119 // while a full-size resample and a mesh displacement happen behind it.
5121 // This window is drawn after the block that consumes that flag, so the rebuild happens on a later
5122 // frame - which means there has to *be* one. Cheap insurance: a slider release that generated no
5123 // further canvas event would otherwise leave the flag set and the model unchanged until something
5124 // unrelated redrew.
5125 m_parent.set_as_dirty();
5126 }
5127
5128 ImGui::Separator();
5129 m_imgui->text_wrapped(_u8L("The model updates when you release a slider. Save as copy writes the "
5130 "adjusted image to your texture library and points this layer at it."), img_w);
5131
5132 const bool cancel = m_imgui->button(_L("Cancel"));
5133 ImGui::SameLine();
5134 // Nothing to save until a slider has actually moved.
5135 m_imgui->disabled_begin(m_adjust.identity());
5136 const bool as_copy = m_imgui->button(_L("Save as copy"));
5137 m_imgui->disabled_end();
5138
5139 ImGui::End();
5140
5141 if (cancel || !keep_open)
5143 else if (as_copy)
5144 commit_image_adjust(*layer);
5145}
void update_image_adjust_preview()
Definition GLGizmoTextureDisplacement.cpp:4925
void commit_image_adjust(TextureDisplacementLayer &layer)
Definition GLGizmoTextureDisplacement.cpp:4961

References _L, _u8L, close_image_adjust(), commit_image_adjust(), Slic3r::GUI::format(), Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, Slic3r::TextureDisplacementLayer::image_data, Slic3r::GUI::into_u8(), invalidate_adjust_textures(), load_image_adjust_source(), m_adjust, m_adjust_applied, m_adjust_preview_tex, m_adjust_preview_valid, m_adjust_slot, m_adjust_source, m_adjust_source_image, m_adjust_volume_id, Slic3r::GUI::GLGizmoBase::m_imgui, Slic3r::GUI::GLGizmoBase::m_parent, m_preview_params_dirty, Slic3r::TextureDisplacementLayer::preview_adjust, Slic3r::TextureDisplacementLayer::slot, Slic3r::ModelVolume::texture_displacement_layers, texture_volume(), and update_image_adjust_preview().

Here is the call graph for this function:

◆ render_input_window()

void Slic3r::GUI::GLGizmoBase::render_input_window ( float x,
float y,
float bottom_limit )
inherited
529{
530 on_render_input_window(x, y, bottom_limit);
532 // imgui windows that don't have an initial size needs to be processed once to get one
533 // and are not rendered in the first frame
534 // so, we forces to render another frame the first time the imgui window is shown
535 // https://github.com/ocornut/imgui/issues/2949
536 m_parent.set_as_dirty();
537 m_parent.request_extra_frame();
539 }
540}
virtual void on_render_input_window(float x, float y, float bottom_limit)
Definition GLGizmoBase.hpp:258
bool m_first_input_window_render
Definition GLGizmoBase.hpp:154

References m_first_input_window_render, m_parent, and on_render_input_window().

Here is the call graph for this function:

◆ render_island_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_island_overlay ( )
private
2122{
2123 const ModelObject *mo = m_c->selection_info()->model_object();
2124 const ModelVolume *mv = texture_volume();
2125 if (mo == nullptr || mv == nullptr || !m_island_overlay_glmodel.is_initialized())
2126 return;
2127 GLShaderProgram *shader = wxGetApp().get_shader("flat");
2128 if (shader == nullptr)
2129 return;
2130 const Selection &selection = m_parent.get_selection();
2131 const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
2132 const Camera &camera = wxGetApp().plater()->get_camera();
2133 shader->start_using();
2134 shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix);
2135 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
2136 // Above the paint tint (a larger offset), translucent, no depth writes - a marker, not geometry.
2138 glsafe(::glPolygonOffset(-2.0f, -2.0f));
2140 m_island_overlay_glmodel.render();
2143 shader->stop_using();
2144}
#define GL_TRUE
Definition gl.h:1892
#define GL_POLYGON_OFFSET_FILL
Definition gl.h:1318
#define glPolygonOffset
Definition gl.h:4471
#define glDepthMask
Definition gl.h:3595
#define GL_FALSE
Definition gl.h:648

References Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::Camera::get_view_matrix(), GL_FALSE, GL_POLYGON_OFFSET_FILL, GL_TRUE, glDepthMask, glDisable, glEnable, glPolygonOffset, glsafe, Slic3r::ModelObject::instances, Slic3r::GUI::GLGizmoBase::m_c, m_island_overlay_glmodel, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), and texture_volume().

Referenced by render_painter_gizmo().

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

◆ render_paint_cursor_hint()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_paint_cursor_hint ( )
private
6997{
6998 // Only in the plain paint/select modes; seam and adjust modes have their own click semantics where
6999 // an add/remove sign would just be noise.
7001 return;
7002 const ImGuiIO &io = ImGui::GetIO();
7003 // The pointer must be over the 3D view, not over this panel (or any other ImGui window).
7004 if (io.WantCaptureMouse || !ImGui::IsMousePosValid())
7005 return;
7006
7007 // Shift erases (see handle_snapshot_action_name()), and so does a plain stroke in Erase mode.
7008 const bool removing = io.KeyShift || m_erase_mode;
7009 const ImU32 color = removing ? IM_COL32(235, 70, 60, 255) : IM_COL32(90, 210, 110, 255);
7010 const char *glyph = removing ? "-" : "+";
7011
7012 ImDrawList *dl = ImGui::GetForegroundDrawList();
7013 const float fs = ImGui::GetFontSize() * 1.5f;
7014 const ImVec2 at(io.MousePos.x + 15.f, io.MousePos.y - fs - 6.f);
7015 // A translucent dark disc behind the glyph so it reads on any material colour.
7016 dl->AddCircleFilled(ImVec2(at.x + fs * 0.28f, at.y + fs * 0.5f), fs * 0.62f, IM_COL32(0, 0, 0, 150));
7017 dl->AddText(ImGui::GetFont(), fs, at, color, glyph);
7018}

References m_adjust_texture_mode, m_erase_mode, and m_seam_edit_mode.

◆ render_paint_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_paint_overlay ( GLModel & overlay)
private
2035{
2036 const ModelObject *mo = m_c->selection_info()->model_object();
2037 const ModelVolume *mv = texture_volume();
2038 if (mo == nullptr || mv == nullptr || !overlay.is_initialized())
2039 return;
2040 GLShaderProgram *shader = wxGetApp().get_shader("flat");
2041 if (shader == nullptr)
2042 return;
2043
2044 const Selection &selection = m_parent.get_selection();
2045 const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
2046 const Camera &camera = wxGetApp().plater()->get_camera();
2047
2048 shader->start_using();
2049 shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix);
2050 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
2051 // Translucent, and pulled toward the camera so it wins the depth test against the coincident
2052 // shaded surface - and against the selectors' highlight at -1 in the Normal view, by the full depth
2053 // unit OpenGL guarantees to tell apart. Depth writes are off: this is a tint, and letting it own the
2054 // depth buffer would make the wireframe and seam overlays drawn after it fight with geometry that is
2055 // not really there. Blending is already enabled by render_painter_gizmo().
2057 glsafe(::glPolygonOffset(-2.f, -2.f));
2059 overlay.render();
2062 shader->stop_using();
2063}

References Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::Camera::get_view_matrix(), GL_FALSE, GL_POLYGON_OFFSET_FILL, GL_TRUE, glDepthMask, glDisable, glEnable, glPolygonOffset, glsafe, Slic3r::ModelObject::instances, Slic3r::GUI::GLModel::is_initialized(), Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::GUI::GLModel::render(), Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), and texture_volume().

Referenced by render_painter_gizmo().

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

◆ render_painted_colors()

bool Slic3r::GUI::GLGizmoTextureDisplacement::render_painted_colors ( bool whole_stack)
private
1949{
1950 rebuild_painted_colors(whole_stack);
1951 const ModelObject *mo = m_c->selection_info()->model_object();
1952 const ModelVolume *mv = texture_volume();
1953 GLShaderProgram *shader = wxGetApp().get_shader("mm_gouraud");
1954 if (mo == nullptr || mv == nullptr || shader == nullptr || !m_painted_colors_glmodel.is_initialized())
1955 return false;
1956
1957 const Selection &selection = m_parent.get_selection();
1958 const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
1959 const Camera &camera = wxGetApp().plater()->get_camera();
1960 const Transform3d &view_matrix = camera.get_view_matrix();
1961 const Matrix3d normal_matrix = trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
1962 const std::vector<ColorRGBA> colors = wxGetApp().plater()->get_extruders_colors();
1963
1964 shader->start_using();
1965 // Set up as render_triangles() sets it up, so the colours are lit, clipped and slope-marked exactly as the
1966 // neutral surface they cover.
1968 shader->set_uniform("clipping_plane", clp_data.clp_dataf);
1969 shader->set_uniform("z_range", clp_data.z_range);
1970 shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix);
1971 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
1972 shader->set_uniform("view_normal_matrix", Matrix3d(view_matrix.matrix().block(0, 0, 3, 3) * normal_matrix));
1973 shader->set_uniform("volume_world_matrix", trafo_matrix);
1974 shader->set_uniform("volume_mirrored", trafo_matrix.matrix().determinant() < 0.);
1975 shader->set_uniform("slope.actived", m_parent.is_using_slope());
1976 shader->set_uniform("slope.volume_world_normal_matrix", Matrix3f(normal_matrix.cast<float>()));
1977 shader->set_uniform("slope.normal_z", float(-std::cos(Geometry::deg2rad(m_highlight_by_angle_threshold_deg))));
1978 shader->set_uniform("slope.up_direction", get_tilt_up_direction());
1979 shader->set_uniform("show_wireframe", false);
1980 // Pulled forward and without depth writes, as the tint is (see render_paint_overlay()). The tint is
1981 // drawn after this, so it shows on top.
1983 glsafe(::glPolygonOffset(-2.f, -2.f));
1985 bool drawn = false;
1986 for (const auto &[state, range] : m_painted_colors_runs)
1987 if (state >= 1 && size_t(state) <= colors.size()) {
1988 m_painted_colors_glmodel.set_color(adjust_color_for_rendering(colors[size_t(state - 1)]));
1989 m_painted_colors_glmodel.render(range, shader);
1990 drawn = true;
1991 }
1994 shader->stop_using();
1995 return drawn;
1996}
Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::ColorRGBA &colors)
Definition 3DScene.cpp:112
ClippingPlaneDataWrapper get_clipping_plane_data() const
Definition GLGizmoPainterBase.cpp:90
void rebuild_painted_colors(bool whole_stack)
Definition GLGizmoTextureDisplacement.cpp:1885
constexpr T deg2rad(const T angle)
Definition Geometry.hpp:312
Eigen::Matrix< float, 3, 3, Eigen::DontAlign > Matrix3f
Definition Point.hpp:85

References adjust_color_for_rendering(), Slic3r::GUI::GLGizmoPainterBase::ClippingPlaneDataWrapper::clp_dataf, Slic3r::Geometry::deg2rad(), Slic3r::GUI::GLGizmoPainterBase::get_clipping_plane_data(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::GLGizmoPainterBase::get_tilt_up_direction(), Slic3r::GUI::Camera::get_view_matrix(), GL_FALSE, GL_POLYGON_OFFSET_FILL, GL_TRUE, glDepthMask, glDisable, glEnable, glPolygonOffset, glsafe, Slic3r::ModelObject::instances, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoPainterBase::m_highlight_by_angle_threshold_deg, m_painted_colors_glmodel, m_painted_colors_runs, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::range(), rebuild_painted_colors(), Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), texture_volume(), and Slic3r::GUI::GLGizmoPainterBase::ClippingPlaneDataWrapper::z_range.

Referenced by render_painter_gizmo().

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

◆ render_painter_gizmo()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_painter_gizmo ( )
overridevirtual

Implements Slic3r::GUI::GLGizmoPainterBase.

668{
669 const Selection &selection = m_parent.get_selection();
670
673
674 // Once anything is painted, m_preview_glmodel holds the true displaced result (same algorithm
675 // Bake uses). The untouched original topology (what render_triangles() draws) coincides exactly
676 // with it everywhere except the displaced area, so both can be drawn: the real preview geometry
677 // first, then the selectors' highlight with a small depth bias so it wins the depth test on the
678 // coincident surface. Where the surface has actually been displaced, the raised preview geometry
679 // legitimately occludes the flat highlight - that visible relief is itself the "this is painted"
680 // indicator in that area.
681 //
682 // The shaded preview never actually moves geometry (it only shades), so its depth is identical to
683 // the highlight's *everywhere*: the depth-biased opaque highlight would win the depth test across the
684 // whole surface and hide the shading entirely. So render_triangles() leaves the textured volume out
685 // there, and the translucent tint drawn further down is the paint feedback instead.
686 // Coalesced shaded-preview rebuild from an in-progress UV island drag (see on_island_edited): done here, at
687 // most once per drawn frame, rather than synchronously in the UV canvas's mouse-move handler.
691 }
692 // Same coalescing for the paint tint, but on its own flag: a stroke marks this every mouse move
693 // (see on_mouse()) and it only costs the painted patch
696 m_paint_overlay_dirty = false;
697 }
698 rebuild_other_paint_overlay(); // a no-op unless another layer's paint, the active layer or the preview changed
699 // is_initialized() alone is not enough: render_shaded_preview_mesh() also needs an active layer
700 // with a decoded texture and a compiled shader, and bails silently without them. Hiding the real
701 // volume for a shaded pass that then draws nothing is what made the model vanish - most obviously
702 // with zero layers, but equally with a layer that has no texture picked yet.
703 const bool use_shaded = m_use_shaded_preview && m_shaded_preview_glmodel.is_initialized() && shaded_preview_ready();
704 // Checker/Distortion are built from the *base* patch and drawn with a polygon offset, which biases
705 // depth values - it does not move the geometry. It therefore cannot win against a surface that
706 // genuinely stands in front, and the displaced preview does exactly that: it rises above the base
707 // surface by the layer's depth. Drawn underneath a UV-check overlay it simply occludes it, which is
708 // why those two modes looked like they did nothing. Leave it out and let the undisplaced volume show
709 // through instead (toggle_model_objects_visibility below) - that one *is* coincident with the
710 // overlay, which is what the offset assumes, and it is the surface whose mapping is being inspected.
711 const bool use_true_preview = !use_shaded && m_uv_check_mode == UVCheckMode::None && has_true_preview();
712 // In Checker/Distortion mode the UV-check overlay *is* the surface visualization the user is
713 // looking at, so the opaque paint-selection highlight must not be drawn on top of it - same
714 // reasoning as skipping it for the shaded preview (see bug #12). Without this the painted area
715 // covers the checker/heatmap and it can't be seen.
716 const bool show_paint_overlay = m_uv_check_mode == UVCheckMode::None;
717
718 // Hide the real volume only when something is actually going to be drawn in its place; otherwise
719 // put it back. Getting this wrong leaves an invisible model, so it is decided once, here, rather
720 // than per branch below.
721 ModelVolume *mv = texture_volume();
722 // The parts a preview is standing in for, and whose real volumes therefore have to be hidden and
723 // whose selectors must not be drawn over it. The shaded view only ever shades the layer stack's own
724 // part; the true-displacement view covers every painted part of a multibody object.
725 std::vector<const ModelVolume *> previewed;
726 if (use_shaded && mv != nullptr) {
727 previewed.push_back(mv);
728 } else if (use_true_preview) {
729 if (mv != nullptr && m_preview_glmodel.is_initialized())
730 previewed.push_back(mv);
731 for (const std::unique_ptr<PartPreview> &preview : m_part_previews)
732 if (const ModelVolume *part = preview_part(*preview); part != nullptr)
733 previewed.push_back(part);
734 }
735 m_parent.toggle_model_objects_visibility(true);
736 for (const ModelVolume *volume : previewed)
737 m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(),
738 m_c->selection_info()->get_active_instance(), volume);
739
740 // Over a preview, the paint feedback - the selectors' highlight in the Normal view, the tint in both -
741 // is drawn only while a mouse button is down: during a stroke, a fill click included. Between strokes
742 // it would cover the preview: the highlight buries it under a flat plane wherever the relief does not
743 // rise, and the tint washes it green. The debug view shows a captured stage rather than a preview, so
744 // it keeps the feedback.
745 const bool paint_feedback = is_painting() || m_debug_stage >= 0;
746
747 // Whether the textured volume's selector - and with it a fill tool's contour - is drawn this frame.
748 bool textured_selector_drawn = true;
749 if (use_shaded) {
751 // The shaded mesh is the textured volume alone, so the other model parts are still the selectors' to draw.
752 render_triangles(selection, previewed);
753 textured_selector_drawn = false;
754 } else if (use_true_preview) {
757 glsafe(::glPolygonOffset(-1.0f, -1.0f));
758 // Without paint feedback, only the parts with no preview over them: render_preview_mesh() draws
759 // the rest.
760 render_triangles(selection, paint_feedback ? std::vector<const ModelVolume *>{} : previewed);
762 textured_selector_drawn = paint_feedback;
763 } else {
764 // render_triangles() *is* the model in a painter gizmo (it draws every model-part volume with the
765 // selector's colours), not an overlay on top of one - so it still has to run under a UV-check
766 // overlay, or nothing draws the surface at all and the checker floats alone over an empty scene.
767 // Deliberately without the depth bias the branch above applies: the checker/heatmap is drawn later
768 // with its own -1 offset and has to win against this. Biasing both by the same amount is what made
769 // the painted area cover the checker and is why this call used to be skipped outright.
770 render_triangles(selection);
771 }
772
773 // The model's colour paint, over whichever surface was drawn, left out where that surface's preview shows
774 // paint of its own: every layer's for the Normal mesh, the active layer's otherwise.
775 const bool painted_colors_drawn = show_paint_overlay && m_debug_stage < 0 && render_painted_colors(use_true_preview);
776
777 // Every other layer's paint, in muted grey, so all layers stay visible while one of them is edited. Drawn
778 // before the active layer's tint so that one reads on top where the two overlap.
779 if (show_paint_overlay)
781
782 // The translucent paint tint. Needed in the shaded view because the opaque highlight above is
783 // skipped there, and in the true-displacement view because the displaced surface rises *above*
784 // the undisplaced overlay geometry and hides it exactly where the relief is strongest - in both
785 // cases leaving an erase stroke with no visible effect until the next full preview rebuild. With no
786 // preview, the highlight is the selectors' own, except during a stroke over the colour paint: the paint
787 // there is not flushed yet, so the colours are still drawn over the stroke's highlight.
788 const bool preview_drawn = use_shaded || use_true_preview;
789 if (show_paint_overlay && (preview_drawn ? paint_feedback : painted_colors_drawn && is_painting()))
791
792 // A fill tool's contour is drawn with the selectors, under the colour paint drawn since - which covers it
793 // on a steep face, where the paint's slope-scaled offset outruns the contour's fixed one. Put it back on top,
794 // at the depth its first draw stored, hence LEQUAL. Only where the selector was drawn this frame: drawing it
795 // is what rebuilds the contour. The tool test matters because the base keeps the last hovered mesh when the
796 // tool changes.
797 const bool fill_tool = m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL ||
799 const int textured_mesh_id = texture_volume_raycaster_index();
800 if (painted_colors_drawn && textured_selector_drawn && fill_tool && textured_mesh_id >= 0 &&
801 textured_mesh_id == m_seed_fill_last_mesh_id && size_t(textured_mesh_id) < m_triangle_selectors.size()) {
802 const ModelObject *mo = m_c->selection_info()->model_object();
803 GLint depth_func = GL_LESS;
804 glsafe(::glGetIntegerv(GL_DEPTH_FUNC, &depth_func));
806 m_triangle_selectors[size_t(textured_mesh_id)]->render_paint_contour(
807 mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix());
808 glsafe(::glDepthFunc(GLenum(depth_func)));
809 }
810
811 // The UV editor's island selection, shown on the model. Polled here rather than pushed: the pane
812 // changes its selection in its own mouse handling, and a compare of a few ints per frame is free.
813 if (const std::vector<int> *islands = island_highlight_selection()) {
814 if (*islands != m_island_overlay_selection)
815 rebuild_island_overlay(*islands);
817 } else if (m_island_overlay_glmodel.is_initialized()) {
820 }
821
822 // Diagnostic overlays, drawn on top of whatever preview is active (both pull toward the camera
823 // with a polygon offset so they win the depth test against the coincident surface).
826 // While previewing a subdivision, its wireframe stands in for the mesh wireframe - it shows the
827 // density the model *would* have. The normal wireframe toggle is left untouched underneath, so it
828 // returns to whatever it was once the preview ends (which is what keeps an already-on wireframe on).
831 else if (m_wireframe_overlay)
833 // Marked seams are always shown for an LSCM layer, so existing cuts are visible before entering
834 // seam-edit mode - but they matter most while marking.
836
837 m_c->object_clipper()->render_cut();
838 m_c->instances_hider()->render_cut();
841 else if (!m_seam_edit_mode) // the brush cursor is meaningless while marking seams
843
845}
virtual void render_triangles(const Selection &selection, const ModelVolume *skip=nullptr) const
Definition GLGizmoPainterBase.cpp:116
bool is_painting() const
Definition GLGizmoPainterBase.hpp:339
void render_cursor()
Definition GLGizmoPainterBase.cpp:218
bool render_painted_colors(bool whole_stack)
Definition GLGizmoTextureDisplacement.cpp:1948
void render_paint_overlay(GLModel &overlay)
Definition GLGizmoTextureDisplacement.cpp:2034
void rebuild_other_paint_overlay()
Definition GLGizmoTextureDisplacement.cpp:1811
void rebuild_island_overlay(const std::vector< int > &selection)
Definition GLGizmoTextureDisplacement.cpp:2075
void render_seam_overlay()
Definition GLGizmoTextureDisplacement.cpp:1193
int texture_volume_raycaster_index() const
Definition GLGizmoTextureDisplacement.cpp:924
void render_uvcheck_mesh()
Definition GLGizmoTextureDisplacement.cpp:2241
void render_adjust_texture_gizmo()
Definition GLGizmoTextureDisplacement.cpp:3554
const ModelVolume * preview_part(const PartPreview &preview) const
Definition GLGizmoTextureDisplacement.cpp:1302
void render_island_overlay()
Definition GLGizmoTextureDisplacement.cpp:2121
bool has_true_preview() const
Definition GLGizmoTextureDisplacement.hpp:217
void render_wireframe_overlay()
Definition GLGizmoTextureDisplacement.cpp:2352
void render_subdivide_preview()
Definition GLGizmoTextureDisplacement.cpp:6515
bool shaded_preview_ready() const
Definition GLGizmoTextureDisplacement.cpp:1777
void render_preview_mesh()
Definition GLGizmoTextureDisplacement.cpp:1255
void render_shaded_preview_mesh()
Definition GLGizmoTextureDisplacement.cpp:1643
#define GL_LESS
Definition gl.h:914
#define GL_DEPTH_FUNC
Definition gl.h:567
#define glDepthFunc
Definition gl.h:3593
int GLint
Definition gl.h:2062
#define glGetIntegerv
Definition gl.h:3873
unsigned int GLenum
Definition gl.h:2054
#define GL_LEQUAL
Definition gl.h:913

References Slic3r::GUI::GLGizmoPainterBase::BRUSH, Slic3r::GUI::GLGizmoPainterBase::BUCKET_FILL, Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), GL_BLEND, GL_DEPTH_FUNC, GL_DEPTH_TEST, GL_LEQUAL, GL_LESS, GL_POLYGON_OFFSET_FILL, glDepthFunc, glDisable, glEnable, glGetIntegerv, glPolygonOffset, glsafe, has_true_preview(), Slic3r::ModelObject::instances, Slic3r::GUI::GLGizmoPainterBase::is_painting(), island_highlight_selection(), m_adjust_texture_mode, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoPainterBase::m_cursor_type, m_debug_stage, m_island_overlay_glmodel, m_island_overlay_selection, m_other_paint_glmodel, m_paint_overlay_dirty, m_paint_overlay_glmodel, Slic3r::GUI::GLGizmoBase::m_parent, m_part_previews, m_preview_glmodel, m_seam_edit_mode, Slic3r::GUI::GLGizmoPainterBase::m_seed_fill_last_mesh_id, m_shaded_preview_dirty, m_shaded_preview_glmodel, m_subdivide_editing, Slic3r::GUI::GLGizmoPainterBase::m_tool_type, Slic3r::GUI::GLGizmoPainterBase::m_triangle_selectors, m_use_shaded_preview, m_uv_check_mode, m_wireframe_overlay, None, Slic3r::TriangleSelector::POINTER, preview_part(), rebuild_island_overlay(), rebuild_other_paint_overlay(), rebuild_paint_overlay(), rebuild_shaded_preview_mesh(), render_adjust_texture_gizmo(), Slic3r::GUI::GLGizmoPainterBase::render_cursor(), render_island_overlay(), render_paint_overlay(), render_painted_colors(), render_preview_mesh(), render_seam_overlay(), render_shaded_preview_mesh(), render_subdivide_preview(), Slic3r::GUI::GLGizmoPainterBase::render_triangles(), render_uvcheck_mesh(), render_wireframe_overlay(), shaded_preview_ready(), Slic3r::GUI::GLGizmoPainterBase::SMART_FILL, texture_volume(), and texture_volume_raycaster_index().

Here is the call graph for this function:

◆ render_preview_mesh()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_preview_mesh ( )
private
1256{
1257 const ModelObject *mo = m_c->selection_info()->model_object();
1258 if (mo == nullptr)
1259 return;
1260
1261 auto *shader = wxGetApp().get_shader("gouraud_light");
1262 if (shader == nullptr)
1263 return;
1264
1265 const Selection &selection = m_parent.get_selection();
1266 const Transform3d instance_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix();
1267 const Camera &camera = wxGetApp().plater()->get_camera();
1268 const Transform3d &view_matrix = camera.get_view_matrix();
1269
1270 shader->start_using();
1271 // Each part is drawn through its own matrix: a multibody object's parts sit in different places
1272 // inside the object, so one matrix for all of them would pile every preview onto the first part.
1273 const auto draw = [&](const ModelVolume &volume, GLModel &model, const std::vector<PreviewColorRun> &runs) {
1274 if (!model.is_initialized())
1275 return;
1276 const Transform3d trafo_matrix = instance_matrix * volume.get_matrix();
1277 shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix);
1278 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
1279 const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) *
1280 trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
1281 shader->set_uniform("view_normal_matrix", view_normal_matrix);
1282 if (runs.empty()) {
1283 model.render();
1284 } else {
1285 // set_color() writes the uniform the shader reads, so one call per group is all the
1286 // per-triangle colour this needs.
1287 for (const PreviewColorRun &run : runs) {
1288 model.set_color(run.color);
1289 model.render(run.range, shader);
1290 }
1291 }
1292 };
1293
1294 if (const ModelVolume *owner = texture_volume(); owner != nullptr)
1296 for (const std::unique_ptr<PartPreview> &preview : m_part_previews)
1297 if (const ModelVolume *part = preview_part(*preview); part != nullptr)
1298 draw(*part, preview->glmodel, preview->color_runs);
1299 shader->stop_using();
1300}
Definition GLGizmoTextureDisplacement.hpp:192
AppActionRunResult run(const std::string &key, const std::string &param={})
Definition NativeCommands.cpp:668
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

References Slic3r::GUI::draw(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::Camera::get_view_matrix(), Slic3r::ModelObject::instances, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, m_part_previews, m_preview_color_runs, m_preview_glmodel, preview_part(), and texture_volume().

Referenced by render_painter_gizmo().

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

◆ render_seam_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_seam_overlay ( )
private
1194{
1195 const ModelObject *mo = m_c->selection_info()->model_object();
1196 const ModelVolume *mv = texture_volume();
1197 const bool have_marked = m_seam_glmodel.is_initialized();
1198 const bool have_hover = m_seam_edit_mode && m_seam_hover_glmodel.is_initialized();
1199 const bool have_anchor = m_seam_edit_mode && m_seam_anchor_glmodel.is_initialized();
1200 if (mo == nullptr || mv == nullptr || (!have_marked && !have_hover && !have_anchor))
1201 return;
1202 GLShaderProgram *shader = wxGetApp().get_shader("flat");
1203 if (shader == nullptr)
1204 return;
1205
1206 const Selection &selection = m_parent.get_selection();
1207 const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
1208 const Camera &camera = wxGetApp().plater()->get_camera();
1209
1210 shader->start_using();
1211 shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix);
1212 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
1214 glsafe(::glPolygonOffset(-2.0f, -2.0f)); // pull further forward than the wireframe so seams read on top
1215 // A seam edge is geometrically the same line as a wireframe edge, so a mere polygon offset is a
1216 // fragile way to make the red seam beat the white wireframe - drivers apply GL_POLYGON_OFFSET_LINE
1217 // inconsistently, and the two lines then z-fight and the wireframe wins. When the wireframe is on,
1218 // or while actively marking, just draw the seams with depth testing off so they are unconditionally
1219 // on top - being visible is the one thing this overlay has to guarantee.
1220 const bool seams_on_top = m_wireframe_overlay || m_seam_edit_mode;
1221 if (seams_on_top)
1223#if !SLIC3R_OPENGL_ES
1224 const bool wide = !OpenGLManager::get_gl_info().is_core_profile();
1225 if (wide)
1226 glsafe(::glLineWidth(4.0f));
1227#endif // !SLIC3R_OPENGL_ES
1228 if (have_marked) {
1229 m_seam_glmodel.set_color(ColorRGBA(1.0f, 0.15f, 0.15f, 1.0f)); // Blender's seam red
1230 m_seam_glmodel.render();
1231 }
1232 // The edge a click would toggle, in yellow and pulled the furthest forward, so it is unmistakable
1233 // which edge is being targeted while marking seams.
1234 if (have_hover) {
1235 glsafe(::glPolygonOffset(-3.0f, -3.0f));
1236 m_seam_hover_glmodel.set_color(ColorRGBA(1.0f, 0.9f, 0.15f, 1.0f));
1237 m_seam_hover_glmodel.render();
1238 }
1239 // The shortest-path start vertex, shown as its ring of incident edges in green.
1240 if (have_anchor) {
1241 glsafe(::glPolygonOffset(-3.0f, -3.0f));
1242 m_seam_anchor_glmodel.set_color(ColorRGBA(0.2f, 1.0f, 0.4f, 1.0f));
1243 m_seam_anchor_glmodel.render();
1244 }
1245#if !SLIC3R_OPENGL_ES
1246 if (wide)
1247 glsafe(::glLineWidth(1.0f));
1248#endif // !SLIC3R_OPENGL_ES
1250 if (seams_on_top)
1252 shader->stop_using();
1253}
#define GL_POLYGON_OFFSET_LINE
Definition gl.h:1319

References Slic3r::GUI::OpenGLManager::get_gl_info(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::Camera::get_view_matrix(), GL_DEPTH_TEST, GL_POLYGON_OFFSET_LINE, glDisable, glEnable, glLineWidth, glPolygonOffset, glsafe, Slic3r::ModelObject::instances, Slic3r::GUI::OpenGLManager::GLInfo::is_core_profile(), Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, m_seam_anchor_glmodel, m_seam_edit_mode, m_seam_glmodel, m_seam_hover_glmodel, m_wireframe_overlay, Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), and texture_volume().

Referenced by render_painter_gizmo().

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

◆ render_shaded_preview_mesh()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_shaded_preview_mesh ( )
private
1644{
1645 const ModelObject *mo = m_c->selection_info()->model_object();
1646 const ModelVolume *mv = texture_volume();
1647 if (mo == nullptr || mv == nullptr || !m_shaded_preview_glmodel.is_initialized())
1648 return;
1649
1650 const TextureDisplacementLayer *layer = active_layer();
1651 if (layer == nullptr || layer->empty())
1652 return;
1653
1654 // Full-resolution height upload (smoothing-aware), whose grayscale value lives in the R channel
1655 // exactly as the shader samples it. Deliberately *not* the layer-list panel's thumbnail: that one
1656 // is box-filtered down to 128 px for a ~48 px row, and feeding it to the shader cost the preview
1657 // three quarters of the height map's detail - and, since height_tex_texel is derived from it, also
1658 // flattened the shading gradient and made the parallax march skip itself at angles where it should
1659 // run. Cached per image_data and smoothing, so no PNG is decoded per frame.
1660 GLTexture *tex = get_layer_height_texture(*layer);
1661 if (tex == nullptr || tex->get_width() <= 0 || tex->get_height() <= 0)
1662 return;
1663
1664 GLShaderProgram *shader = wxGetApp().get_shader("texture_displacement_shaded");
1665 if (shader == nullptr)
1666 return;
1667
1668 const Selection &selection = m_parent.get_selection();
1669 const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
1670 const Camera &camera = wxGetApp().plater()->get_camera();
1671
1672 shader->start_using();
1673 shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix);
1674 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
1675 shader->set_uniform("volume_world_matrix", trafo_matrix);
1676 const ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data();
1677 shader->set_uniform("clipping_plane", clp_data.clp_dataf);
1678 shader->set_uniform("z_range", clp_data.z_range);
1679 // The shader works on world-space normals (it projects the texture in world millimetres, as the
1680 // bake does), so the normal matrix is the view rotation alone - no model part.
1681 const Matrix3d view_normal_matrix = camera.get_view_matrix().matrix().block(0, 0, 3, 3);
1682 shader->set_uniform("view_normal_matrix", view_normal_matrix);
1683 shader->set_uniform("volume_mirrored", trafo_matrix.matrix().determinant() < 0.0);
1685 glsafe(::glBindTexture(GL_TEXTURE_2D, tex->get_id()));
1686 // Match DecodedHeightTexture::sample()'s tiling. The sampler's wrap mode is the only place the
1687 // GPU path can express this, and nothing ever set it - so it sat at GL_REPEAT no matter what the
1688 // layer said: a MirroredRepeat layer previewed as a plain repeat, and a layer with tiling *off*
1689 // previewed as an endless tiling where the bake produces one placement and nothing around it.
1690 // CLAMP_TO_BORDER with a zero border is the exact analogue of sample()'s "outside [0,1) is 0".
1691 // GL_CLAMP_TO_BORDER is desktop-GL only; on ES the nearest thing is CLAMP_TO_EDGE, which smears
1692 // the border row instead of vanishing - still much closer to the bake than an endless repeat.
1693#if SLIC3R_OPENGL_ES
1694 const GLint no_tile_wrap = GL_CLAMP_TO_EDGE;
1695#else
1696 const GLint no_tile_wrap = GL_CLAMP_TO_BORDER;
1697#endif
1698 const GLint wrap = !layer->tile_enabled ? no_tile_wrap :
1699 (layer->tile_method == TextureTileMethod::MirroredRepeat) ? GL_MIRRORED_REPEAT :
1700 GL_REPEAT;
1703#if !SLIC3R_OPENGL_ES
1704 if (wrap == GL_CLAMP_TO_BORDER) {
1705 static const GLfloat border[4] = { 0.f, 0.f, 0.f, 0.f };
1707 }
1708#endif // !SLIC3R_OPENGL_ES
1709 shader->set_uniform("height_tex", 0);
1710 shader->set_uniform("height_tex_texel", Vec2f(1.f / float(tex->get_width()), 1.f / float(tex->get_height())));
1711 shader->set_uniform("depth_mm", layer->depth_mm);
1712 shader->set_uniform("tiling_scale", layer->tiling_scale);
1713 // Read off the uploaded texture rather than the decoded one: they are the same image, and this is
1714 // the aspect the sampler will actually see.
1715 shader->set_uniform("tex_aspect", float(tex->get_width()) / float(tex->get_height()));
1716 shader->set_uniform("rotation_rad", layer->rotation_deg * float(M_PI) / 180.f);
1717 shader->set_uniform("uv_offset", layer->offset);
1718 shader->set_uniform("invert", layer->invert);
1719 // The parallax step in the triplanar path needs the real height, not just its gradient, so it
1720 // needs the midlevel the bake subtracts - and the camera position in the volume's own local space,
1721 // to build the view ray it walks along. Without the parallax the pattern is welded to the base
1722 // surface: it does not slide as the camera orbits and does not deepen with depth_mm, which is
1723 // exactly when the fast preview stops looking like geometry.
1724 shader->set_uniform("midlevel", layer->midlevel);
1725 // The texture frame is world space anchored at the volume's origin (texture_displacement_bake_frame()):
1726 // the shader subtracts the anchor from the world position, and the eye is handed over the same way.
1727 const Vec3d tex_anchor = trafo_matrix.translation();
1728 shader->set_uniform("tex_anchor", Vec3f(tex_anchor.cast<float>()));
1729 shader->set_uniform("eye_model_pos", Vec3f((camera.get_position() - tex_anchor).cast<float>()));
1730 // When set, the shader samples at the per-vertex uv baked into the mesh (LSCM) rather than
1731 // projecting; see rebuild_shaded_preview_mesh().
1732 shader->set_uniform("use_vertex_uv", m_shaded_preview_uses_vertex_uv);
1733 // Cylindrical/Spherical wrap around the painted patch's own centre, which no fragment can derive:
1734 // captured with the mesh (see rebuild_shaded_preview_mesh()) and handed over here. 0 is the planar
1735 // projection every other in-shader path uses.
1736 shader->set_uniform("projection_mode", m_shaded_projection_mode);
1737 shader->set_uniform("patch_center", m_shaded_patch_center);
1738 shader->set_uniform("patch_axis", m_shaded_patch_axis);
1739
1740 // The printable palette, in RGB for display and in Lab for the match. Uploaded rather than matched
1741 // on the CPU because the quantization is per fragment here. Count 0 means "no layer is colouring",
1742 // and the shader keeps the model's own colour for every fragment.
1743 const GLTexture *color_tex = get_layer_color_texture(*layer);
1744 const int palette_count =
1745 (color_tex != nullptr) ? int(std::min(m_shaded_preview_palette.size(), size_t(PALETTE_MAX_ENTRIES))) : 0;
1746 shader->set_uniform("palette_count", palette_count);
1747 shader->set_uniform("has_color_tex", color_tex != nullptr);
1748 // A flat-colour image is matched against single filaments only, as the bake does.
1749 shader->set_uniform("pure_only", color_tex != nullptr && analyze_texture_detail(*layer).flat_colors);
1750 shader->set_uniform("prefer_pure_de", PREFER_PURE_DE);
1751 for (int i = 0; i < palette_count; ++i) {
1752 const PaletteEntry &e = m_shaded_preview_palette[size_t(i)];
1753 const std::string idx = "[" + std::to_string(i) + "]";
1754 // An entry's colour is what it prints as: its filament's, or for a mix its mixed filament slot's.
1755 shader->set_uniform(("palette_rgb" + idx).c_str(), e.rgb);
1756 shader->set_uniform(("palette_lab" + idx).c_str(), srgb_to_lab(e.rgb));
1757 // Only so the shader can tell a mix (a != b) from a single filament.
1758 shader->set_uniform(("palette_a" + idx).c_str(), e.a);
1759 shader->set_uniform(("palette_b" + idx).c_str(), e.b);
1760 }
1761 if (color_tex != nullptr) {
1762 shader->set_uniform("color_tex", 1);
1764 glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint) color_tex->get_id()));
1766 }
1767 // The live UV-editor island drag rides this 2x3 affine (identity except mid-drag); only the flagged
1768 // island's vertices apply it, so a drag is a uniform update rather than a mesh rebuild.
1769 const Eigen::Matrix<float, 2, 3> &d = m_shaded_island_delta;
1770 shader->set_uniform("island_delta_lin", std::array<float, 4>{ d(0, 0), d(0, 1), d(1, 0), d(1, 1) });
1771 shader->set_uniform("island_delta_tr", Vec2f(d(0, 2), d(1, 2)));
1772 m_shaded_preview_glmodel.render();
1774 shader->stop_using();
1775}
GLTexture * get_layer_height_texture(const TextureDisplacementLayer &layer)
Definition GLGizmoTextureDisplacement.cpp:6583
GLTexture * get_layer_color_texture(const TextureDisplacementLayer &layer)
Definition GLGizmoTextureDisplacement.cpp:6564
#define GL_MIRRORED_REPEAT
Definition gl.h:1187
#define GL_TEXTURE0
Definition gl.h:1705
#define GL_TEXTURE1
Definition gl.h:1706
#define GL_TEXTURE_BORDER_COLOR
Definition gl.h:1762
#define GL_TEXTURE_WRAP_S
Definition gl.h:1854
#define glTexParameteri
Definition gl.h:4911
#define glTexParameterfv
Definition gl.h:4909
khronos_float_t GLfloat
Definition gl.h:2066
#define glBindTexture
Definition gl.h:3299
unsigned int GLuint
Definition gl.h:2063
#define GL_REPEAT
Definition gl.h:1453
#define GL_CLAMP_TO_BORDER
Definition gl.h:321
#define GL_TEXTURE_WRAP_T
Definition gl.h:1855
#define GL_TEXTURE_2D
Definition gl.h:1739
#define GL_CLAMP_TO_EDGE
Definition gl.h:322
#define glActiveTexture
Definition gl.h:3245
@ MirroredRepeat
Definition TextureDisplacement.hpp:51
bool flat_colors
Definition TextureDisplacement.hpp:507

References Slic3r::PrintableColor::a, active_layer(), Slic3r::analyze_texture_detail(), Slic3r::PrintableColor::b, Slic3r::GUI::GLGizmoPainterBase::ClippingPlaneDataWrapper::clp_dataf, Slic3r::TextureDisplacementLayer::depth_mm, Slic3r::TextureDisplacementLayer::empty(), Slic3r::TextureDetail::flat_colors, Slic3r::GUI::GLGizmoPainterBase::get_clipping_plane_data(), Slic3r::GUI::GLTexture::get_height(), Slic3r::GUI::GLTexture::get_id(), Slic3r::GUI::Selection::get_instance_idx(), get_layer_color_texture(), get_layer_height_texture(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_position(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::Camera::get_view_matrix(), Slic3r::GUI::GLTexture::get_width(), GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT, GL_REPEAT, GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, glActiveTexture, glBindTexture, glsafe, glTexParameterfv, glTexParameteri, Slic3r::ModelObject::instances, Slic3r::TextureDisplacementLayer::invert, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, M_PI, m_shaded_island_delta, m_shaded_patch_axis, m_shaded_patch_center, m_shaded_preview_glmodel, m_shaded_preview_palette, m_shaded_preview_uses_vertex_uv, m_shaded_projection_mode, Slic3r::TextureDisplacementLayer::midlevel, Slic3r::MirroredRepeat, Slic3r::TextureDisplacementLayer::offset, Slic3r::PrintableColor::rgb, Slic3r::TextureDisplacementLayer::rotation_deg, Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), texture_volume(), Slic3r::TextureDisplacementLayer::tile_enabled, Slic3r::TextureDisplacementLayer::tile_method, Slic3r::TextureDisplacementLayer::tiling_scale, and Slic3r::GUI::GLGizmoPainterBase::ClippingPlaneDataWrapper::z_range.

Referenced by render_painter_gizmo().

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

◆ render_subdivide_preview()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_subdivide_preview ( )
private
6516{
6517 const ModelObject *mo = m_c->selection_info()->model_object();
6518 const ModelVolume *mv = texture_volume();
6519 if (mo == nullptr || mv == nullptr || !m_subdivide_preview_glmodel.is_initialized())
6520 return;
6521 GLShaderProgram *shader = wxGetApp().get_shader("flat");
6522 if (shader == nullptr)
6523 return;
6524
6525 const Selection &selection = m_parent.get_selection();
6526 const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
6527 const Camera &camera = wxGetApp().plater()->get_camera();
6528
6529 shader->start_using();
6530 shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix);
6531 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
6533 glsafe(::glPolygonOffset(-1.0f, -1.0f));
6534 m_subdivide_preview_glmodel.set_color(ColorRGBA(0.2f, 0.9f, 1.0f, 0.7f)); // cyan, reads as "preview"
6537 shader->stop_using();
6538}

References Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::Camera::get_view_matrix(), GL_POLYGON_OFFSET_LINE, glDisable, glEnable, glPolygonOffset, glsafe, Slic3r::ModelObject::instances, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, m_subdivide_preview_glmodel, Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), and texture_volume().

Referenced by render_painter_gizmo().

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

◆ render_texture_library_popup()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_texture_library_popup ( const ImVec2 & panel_min,
const ImVec2 & panel_max )
private
4391{
4392 ModelVolume *mv = texture_volume();
4393 TextureDisplacementLayer *layer = nullptr;
4394 if (mv != nullptr)
4395 for (TextureDisplacementLayer &l : mv->texture_displacement_layers)
4396 if (l.slot == m_picker_slot)
4397 layer = &l;
4398
4399 static const char *const popup_id = "##texture_library";
4401 m_picker_open_request = false;
4402 if (layer != nullptr)
4403 ImGui::OpenPopup(popup_id);
4404 }
4405
4406 const float tile = std::round(m_imgui->scaled(3.2f));
4407 const float spacing = std::round(m_imgui->scaled(0.4f));
4408 const float grid_w = 4.f * tile + 3.f * spacing;
4409 const float gap = m_imgui->scaled(0.5f);
4410 const float scrollbar_w = m_imgui->scaled(0.5f);
4411 const float ring_w = std::max(2.f, std::round(m_imgui->scaled(0.1f))); // the current texture's ring
4412 const float frame_h = ImGui::GetFrameHeight();
4413 const ImVec4 orca = ImGuiWrapper::COL_ORCA;
4414 const ImGuiStyle &style = ImGui::GetStyle();
4415
4416 // A fixed size rather than a fit-to-content one. The width is the four-column grid, a gutter and the
4417 // slim scrollbar beside it, plus the window padding, so the columns always fit and no horizontal
4418 // scrollbar is needed; it derives from scaled values, so it tracks the DPI like the rest.
4419 const float fixed_w = grid_w + spacing + scrollbar_w + 2.f * style.WindowPadding.x;
4420
4421 // BeginPopup() ORs in ImGuiWindowFlags_AlwaysAutoResize (deps_src/imgui/imgui.cpp:8371) and no flag
4422 // turns it back off. A SetNextWindowSize() overrides it for each axis it sets (imgui.cpp:5983-5996),
4423 // so an explicit size is the only way to fix the popup's size; the grid child below then scrolls.
4424 //
4425 // Top-aligned with the panel, which keeps it clear of the gizmo toolbar above a docked panel, and as
4426 // tall as the panel or 70% of the window, whichever is taller, while keeping a gap inside the window.
4427 // The height follows the window and the panel, never the content, so it does not jump as packs load or
4428 // sections collapse.
4429 const ImGuiViewport *viewport = ImGui::GetMainViewport();
4430 const float vp_top = viewport->Pos.y + gap;
4431 const float vp_bottom = viewport->Pos.y + viewport->Size.y - gap;
4432 const float height = std::max(std::min(std::max(0.70f * viewport->Size.y, panel_max.y - panel_min.y), vp_bottom - vp_top),
4433 2.f * frame_h);
4434 const float popup_y = std::max(vp_top, std::min(panel_min.y, vp_bottom - height));
4435 // Beside the panel rather than over it, so the layer being retextured stays in view: to its left when
4436 // there is room (the docked panel sits against the right edge of the canvas), otherwise to its right.
4437 const float width = viewport->Size.x > 0.f ? std::min(fixed_w, viewport->Size.x) : fixed_w;
4438 const bool left = panel_min.x - gap - width >= 0.f;
4439 const float popup_x = left ? panel_min.x - gap - width : panel_max.x + gap;
4440 // Cond_Always, not Appearing: a viewport resize while the popup is open must move and resize it too.
4441 ImGui::SetNextWindowPos(ImVec2(popup_x, popup_y), ImGuiCond_Always);
4442 ImGui::SetNextWindowSize(ImVec2(width, height), ImGuiCond_Always);
4443 // Drawn as the panel's own sheet: its opaque background and rounding, no border. A popup is filled with
4444 // PopupBg, which the panel's toolbar style leaves at ImGui's translucent near-black - a dark window
4445 // beside a light panel, with whatever it covers showing through. Only Begin reads these, so they are
4446 // popped at once and the tooltips shown from inside keep their own look.
4447 ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg));
4448 ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, style.WindowRounding);
4449 ImGui::PushStyleVar(ImGuiStyleVar_PopupBorderSize, 0.f);
4450 // The window itself never scrolls; the grid child below does.
4451 const bool open = ImGui::BeginPopup(popup_id, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
4452 ImGui::PopStyleVar(2);
4453 ImGui::PopStyleColor();
4454 if (!open)
4455 return;
4456 if (layer == nullptr) {
4457 ImGui::CloseCurrentPopup();
4458 ImGui::EndPopup();
4459 return;
4460 }
4461
4462 // While this popup is up its own tooltips belong beside it, not beside the panel it is already
4463 // covering the left of. Restored before every exit below.
4464 const ImVec2 prev_anchor_min = m_tooltip_anchor_min;
4465 const ImVec2 prev_anchor_max = m_tooltip_anchor_max;
4466 m_tooltip_anchor_min = ImGui::GetWindowPos();
4467 m_tooltip_anchor_max = ImVec2(m_tooltip_anchor_min.x + ImGui::GetWindowSize().x,
4468 m_tooltip_anchor_min.y + ImGui::GetWindowSize().y);
4469
4470 // The header stays put while the grid scrolls under it: the title says what the popup is for, and the
4471 // close button is there for anyone who does not know that a click outside closes it.
4472 bool close_clicked = false;
4473 {
4474 ImDrawList *hdl = ImGui::GetWindowDrawList();
4475 const ImVec2 row = ImGui::GetCursorScreenPos();
4476 ImGui::SetCursorScreenPos(ImVec2(row.x, row.y + 0.5f * (frame_h - ImGui::GetTextLineHeight())));
4477 m_imgui->bold_text(_u8L("Choose a texture"));
4478 const ImVec2 btn(ImGui::GetWindowPos().x + ImGui::GetWindowContentRegionMax().x - frame_h, row.y);
4479 ImGui::SetCursorScreenPos(btn);
4480 close_clicked = ImGui::InvisibleButton("##close", ImVec2(frame_h, frame_h));
4481 const bool hot = ImGui::IsItemHovered();
4482 const ImVec2 c(btn.x + 0.5f * frame_h, btn.y + 0.5f * frame_h);
4483 const float k = std::round(0.18f * frame_h);
4484 const float line_w = std::max(1.f, std::round(m_imgui->scaled(0.08f)));
4485 const ImU32 x_col = ImGui::GetColorU32(ImGuiCol_Text, hot ? 1.f : 0.6f);
4486 if (hot)
4487 hdl->AddCircleFilled(c, 0.5f * frame_h, ImGui::GetColorU32(ImGuiCol_Text, 0.08f));
4488 hdl->AddLine(ImVec2(c.x - k, c.y - k), ImVec2(c.x + k, c.y + k), x_col, line_w);
4489 hdl->AddLine(ImVec2(c.x - k, c.y + k), ImVec2(c.x + k, c.y - k), x_col, line_w);
4490 ImGui::SetCursorScreenPos(ImVec2(row.x, row.y + frame_h + style.ItemSpacing.y));
4491 ImGui::Separator();
4492 }
4493
4494 const std::vector<TextureLibraryEntry> &library = texture_library();
4495 // A reinstalled pack can drop new bytes at a path already cached on this gizmo, and get_library_texture()
4496 // caches a failure as well as a success, so a moved generation makes every cached entry suspect.
4497 if (const uint64_t generation = texture_library_generation(); generation != m_library_generation) {
4498 m_library_textures.clear();
4499 m_library_generation = generation;
4500 }
4501
4502 // The grid scrolls as the panel body does: the same slim scrollbar, and the wheel eased by
4503 // ease_wheel_scroll() instead of ImGui's five-line jumps (hence NoScrollWithMouse).
4504 push_slim_scrollbar_style(scrollbar_w, wxGetApp().dark_mode());
4505 ImGui::BeginChild("##texture_grid", ImVec2(0.f, 0.f), false, ImGuiWindowFlags_NoScrollWithMouse);
4507
4508 ImDrawList *dl = ImGui::GetWindowDrawList(); // the child's, so tiles clip to the grid
4509 std::optional<TextureLibraryEntry> chosen;
4510 std::optional<TextureLibraryEntry> to_remove;
4511 bool import_clicked = false;
4512 bool download_clicked = false;
4513 int loads_left = PICKER_LOADS_PER_FRAME;
4514 bool loads_deferred = false;
4515
4516 // Tiles are placed on the four-column grid explicitly rather than flowed with SameLine(): a user tile
4517 // overlays a remove button on its corner, and that button would otherwise be "the previous item" the
4518 // next tile lines up against.
4519 const auto tile_pos = [&](const ImVec2 &origin, int i) {
4520 return ImVec2(origin.x + float(i % 4) * (tile + spacing), origin.y + float(i / 4) * (tile + spacing));
4521 };
4522 // Leaves the cursor below a grid of `count` tiles, with the grid's area registered as content.
4523 const auto end_grid = [&](const ImVec2 &origin, int count) {
4524 ImGui::SetCursorScreenPos(origin);
4525 if (count > 0) {
4526 const int rows = (count + 3) / 4;
4527 ImGui::Dummy(ImVec2(grid_w, float(rows) * tile + float(rows - 1) * spacing));
4528 }
4529 };
4530 // One section's tiles, in the order the section holds them (shipped alphabetically, pack textures by
4531 // name, the user's own most recently used first - the order texture_library() produced); a user tile
4532 // carries a remove button on hover. Returns how many tiles it laid out, drawn or not.
4533 const auto grid = [&](const ImVec2 &origin, const std::vector<const TextureLibraryEntry *> &entries, bool removable) {
4534 int n = 0;
4535 for (const TextureLibraryEntry *entry : entries) {
4536 const ImVec2 rect_min = tile_pos(origin, n++);
4537 const ImVec2 rect_max = ImVec2(rect_min.x + tile, rect_min.y + tile);
4538 const bool current = entry->path == layer->path;
4539 // The selected tile is centred even when it is off screen, so it is drawn and loaded next frame.
4540 if (current && m_picker_reveal_selected) {
4541 ImGui::SetScrollFromPosY(0.5f * (rect_min.y + rect_max.y) - ImGui::GetWindowPos().y, 0.5f);
4542 m_parent.request_extra_frame();
4543 }
4544 if (!ImGui::IsRectVisible(rect_min, rect_max))
4545 continue;
4546 // Thumbnails decode on this thread, so only a few per frame; the rest fill in on later frames.
4547 const bool cached = m_library_textures.count(entry->path) != 0;
4548 if (!cached && loads_left == 0) {
4549 loads_deferred = true;
4550 dl->AddRectFilled(rect_min, rect_max, ImGui::GetColorU32(ImGuiCol_FrameBg), style.FrameRounding);
4551 continue;
4552 }
4553 if (!cached)
4554 --loads_left;
4555 ImGui::SetCursorScreenPos(rect_min);
4556 ImGui::PushID(entry->path.c_str());
4557 const LibraryTexture *tex = get_library_texture(entry->path);
4558 GLTexture *thumb = tex != nullptr ? tex->thumbnail.get() : nullptr;
4559 bool clicked = false;
4560 if (thumb != nullptr) {
4561 // Centre-crop a non-square image into the square tile instead of squashing it.
4562 const float aspect = thumb->get_height() > 0 ? float(thumb->get_width()) / float(thumb->get_height()) : 1.f;
4563 ImVec2 uv0(0.f, 0.f), uv1(1.f, 1.f);
4564 if (aspect > 1.f) {
4565 uv0.x = 0.5f * (1.f - 1.f / aspect);
4566 uv1.x = 1.f - uv0.x;
4567 } else if (aspect < 1.f) {
4568 uv0.y = 0.5f * (1.f - aspect);
4569 uv1.y = 1.f - uv0.y;
4570 }
4571 clicked = ImGui::ImageButton((ImTextureID) (intptr_t) thumb->get_id(), ImVec2(tile, tile), uv0, uv1, 0);
4572 } else {
4573 clicked = ImGui::Button("?", ImVec2(tile, tile));
4574 }
4575 if (removable)
4576 ImGui::SetItemAllowOverlap(); // the remove button drawn over its corner takes its own clicks
4577 const ImVec2 a = ImGui::GetItemRectMin(), b = ImGui::GetItemRectMax();
4578 const bool hovered = ImGui::IsItemHovered();
4579 // The layer's current texture carries an accent ring, and the tile under the pointer a thinner,
4580 // fainter one. Both are drawn inside the tile, where the grid's clip rect never cuts them.
4581 if (current || hovered) {
4582 const float w = current ? ring_w : std::round(0.5f * ring_w);
4583 dl->AddRect(ImVec2(a.x + 0.5f * w, a.y + 0.5f * w), ImVec2(b.x - 0.5f * w, b.y - 0.5f * w),
4584 ImGui::GetColorU32(current ? orca : ImVec4(orca.x, orca.y, orca.z, 0.6f)), 0.f, 0, w);
4585 }
4586 if (hovered)
4587 panel_tooltip(entry->name);
4588
4589 // Clipped by the grid's clip rect, so a tile scrolled out of the grid or under its scrollbar
4590 // does not offer its remove button. IsMouseHoveringRect() clips by default.
4591 if (removable && ImGui::IsMouseHoveringRect(a, b)) {
4592 const float r = std::round(tile * 0.17f);
4593 const ImVec2 c(b.x - r - 2.f, a.y + r + 2.f);
4594 ImGui::SetCursorScreenPos(ImVec2(c.x - r, c.y - r));
4595 const bool remove_clicked = ImGui::InvisibleButton("##remove", ImVec2(2.f * r, 2.f * r));
4596 const bool hot = ImGui::IsItemHovered();
4597 dl->AddCircleFilled(c, r, hot ? IM_COL32(214, 72, 64, 235) : IM_COL32(0, 0, 0, 170));
4598 const float k = 0.42f * r;
4599 dl->AddLine(ImVec2(c.x - k, c.y - k), ImVec2(c.x + k, c.y + k), IM_COL32_WHITE, 1.5f);
4600 dl->AddLine(ImVec2(c.x - k, c.y + k), ImVec2(c.x + k, c.y - k), IM_COL32_WHITE, 1.5f);
4601 if (hot)
4602 panel_tooltip(_u8L("Remove from My textures"));
4603 if (remove_clicked)
4604 to_remove = *entry;
4605 }
4606 ImGui::PopID();
4607 if (clicked)
4608 chosen = *entry;
4609 }
4610 return n;
4611 };
4612
4613 // Every section collapses; the state is remembered for the app run. Pack keys use pack_name, a
4614 // capability name unique per type across loaded packages and stable across reinstalls. This function-local
4615 // static survives popup closes, never touches app_config, and resets on restart.
4616 static std::map<std::string, bool> s_section_collapsed; // true = collapsed; absent = expanded
4617
4618 const auto split_pack_title = [](const std::string &title) -> std::pair<std::string, std::string> {
4619 size_t split = std::string::npos;
4620 for (size_t i = 0; i + 3 < title.size(); ++i)
4621 if (title[i] == ' ' && (title[i + 1] == 'b' || title[i + 1] == 'B') &&
4622 (title[i + 2] == 'y' || title[i + 2] == 'Y') && title[i + 3] == ' ')
4623 split = i;
4624 if (split == std::string::npos)
4625 return { title, {} };
4626
4627 std::string name = title.substr(0, split);
4628 while (!name.empty() && std::isspace(static_cast<unsigned char>(name.back())))
4629 name.pop_back();
4630 std::string attribution = title.substr(split);
4631 size_t first = 0;
4632 while (first < attribution.size() && std::isspace(static_cast<unsigned char>(attribution[first])))
4633 ++first;
4634 attribution.erase(0, first);
4635 if (name.empty() || attribution.empty())
4636 return { title, {} };
4637 return { name, attribution };
4638 };
4639
4640 // A section heading, one row as wide as the grid: a click anywhere on it collapses or expands the
4641 // section. Child headings indent their contents while keeping their counts right-aligned. Attribution
4642 // lines use a smaller disabled style; the grid follows closer than the next heading does.
4643 // Returns true when the section is open, so the caller knows whether to draw its contents.
4644 const auto section_header = [&](const std::string &key, const std::string &title, size_t count, float indent,
4645 const std::string &attribution, const std::string &full_title) {
4646 bool &collapsed = s_section_collapsed[key]; // default-inserts false -> expanded
4647 const ImVec2 row = ImGui::GetCursorScreenPos();
4648 const bool two_lines = !attribution.empty();
4649 const float line_h = ImGui::GetTextLineHeight();
4650 const float small_font_size = ImGui::GetFontSize() * 0.8f;
4651 const float small_line_h = line_h * 0.8f;
4652 const float row_h = two_lines ? std::max(frame_h, line_h + small_line_h + spacing) : frame_h;
4653 const ImVec2 row_max(row.x + grid_w, row.y + row_h);
4654 if (ImGui::InvisibleButton(key.c_str(), ImVec2(grid_w, row_h)))
4655 collapsed = !collapsed;
4656 const bool hovered = ImGui::IsItemHovered();
4657 if (hovered)
4658 dl->AddRectFilled(row, row_max, ImGui::GetColorU32(ImGuiCol_Text, 0.05f), style.FrameRounding);
4659
4660 const float inset = std::round(0.25f * frame_h);
4661 const float arrow = std::round(ImGui::GetFontSize() * 0.55f);
4662 const float cx = row.x + inset + indent + 0.5f * arrow;
4663 const float cy = row.y + 0.5f * row_h;
4664 const float hs = 0.3f * arrow;
4665 const ImU32 accent = ImGui::GetColorU32(orca);
4666 if (collapsed)
4667 dl->AddTriangleFilled(ImVec2(cx - 0.5f * hs, cy - hs), ImVec2(cx + 0.5f * hs, cy), ImVec2(cx - 0.5f * hs, cy + hs), accent);
4668 else
4669 dl->AddTriangleFilled(ImVec2(cx - hs, cy - 0.5f * hs), ImVec2(cx + hs, cy - 0.5f * hs), ImVec2(cx, cy + 0.5f * hs), accent);
4670
4671 const std::string n = std::to_string(count);
4672 const float n_w = ImGui::CalcTextSize(n.c_str()).x;
4673 const float tx = row.x + inset + indent + arrow + spacing;
4674 const float max_text_w = std::max(0.f, row_max.x - inset - n_w - spacing - tx);
4675 const std::string shown = ellipsize(title, max_text_w);
4676 const std::string shown_attribution = two_lines ? ellipsize(attribution, max_text_w, small_font_size) : std::string{};
4677 const float title_y = two_lines ? row.y + 0.5f * (row_h - line_h - small_line_h)
4678 : row.y + 0.5f * (frame_h - line_h);
4679 const float count_y = row.y + 0.5f * (row_h - line_h);
4680 dl->AddText(ImVec2(tx, title_y), ImGui::GetColorU32(ImGuiCol_Text), shown.c_str());
4681 if (two_lines)
4682 dl->AddText(ImGui::GetFont(), small_font_size, ImVec2(tx, title_y + line_h),
4683 ImGui::GetColorU32(ImGuiCol_TextDisabled), shown_attribution.c_str());
4684 dl->AddText(ImVec2(row_max.x - inset - n_w, count_y), ImGui::GetColorU32(ImGuiCol_TextDisabled), n.c_str());
4685 if (hovered && (shown != title || shown_attribution != attribution))
4686 panel_tooltip(full_title); // a long plugin-supplied name, cut by the fixed width
4687 ImGui::SetCursorScreenPos(ImVec2(row.x, row_max.y + spacing));
4688 return !collapsed;
4689 };
4690
4691 // Built-in is first, plugin packs are grouped under Texture libraries, and My
4692 // textures stays last so its import tile and help line describe that group. Pack names identify both
4693 // their child section and state.
4694 struct Section
4695 {
4696 std::string key;
4697 std::string title;
4698 std::vector<const TextureLibraryEntry *> entries;
4699 };
4700 std::vector<Section> sections;
4701 std::map<std::string, size_t> pack_section; // pack_name -> index in `sections`
4702 sections.push_back({ "builtin:", _u8L("Built-in"), {} });
4703 for (const TextureLibraryEntry &entry : library) {
4704 if (entry.source == TextureSource::Shipped) {
4705 sections.front().entries.push_back(&entry); // the section exists even when the library is empty
4706 continue;
4707 }
4708 if (entry.source == TextureSource::User)
4709 continue; // added below, so My textures stays last
4710 if (entry.source != TextureSource::Plugin)
4711 continue;
4712 const auto [it, inserted] = pack_section.emplace(entry.pack_name, sections.size());
4713 if (inserted)
4714 sections.push_back({ "pack:" + entry.pack_name, entry.pack_name, {} });
4715 sections[it->second].entries.push_back(&entry);
4716 }
4717 sections.push_back({ "user:", _u8L("My textures"), {} });
4718 for (const TextureLibraryEntry &entry : library)
4719 if (entry.source == TextureSource::User)
4720 sections.back().entries.push_back(&entry);
4721
4722 const float child_indent = std::round(m_imgui->scaled(0.8f));
4723 const Section &built_in = sections.front();
4724 if (section_header(built_in.key, built_in.title, built_in.entries.size(), 0.f, {}, built_in.title)) {
4725 const ImVec2 origin = ImGui::GetCursorScreenPos();
4726 const int count = grid(origin, built_in.entries, false);
4727 end_grid(origin, count);
4728 if (count == 0)
4729 ImGui::TextDisabled("%s", _u8L("No textures found.").c_str());
4730 }
4731
4732 size_t subscribed_count = 0;
4733 for (size_t i = 1; i + 1 < sections.size(); ++i)
4734 subscribed_count += sections[i].entries.size();
4735
4736 const std::string subscribed_title = _u8L("Texture libraries");
4737 if (section_header("subscribed:", subscribed_title, subscribed_count, 0.f, {}, subscribed_title)) {
4738 for (size_t i = 1; i + 1 < sections.size(); ++i) {
4739 const Section &pack = sections[i];
4740 const auto [name, attribution] = split_pack_title(pack.title);
4741 if (!section_header(pack.key, name, pack.entries.size(), child_indent, attribution, pack.title))
4742 continue;
4743 const ImVec2 origin = ImGui::GetCursorScreenPos();
4744 const int count = grid(origin, pack.entries, false);
4745 end_grid(origin, count);
4746 }
4747
4748 ImGui::Dummy(ImVec2(0.f, spacing));
4749 const std::string download_label = _u8L("Explore more texture libraries");
4750 if (ImGui::Button(download_label.c_str(), ImVec2(grid_w, frame_h)))
4751 download_clicked = true;
4752 if (ImGui::IsItemHovered())
4753 panel_tooltip(_u8L("Opens OrcaCloud in the Home page to browse for plugins."));
4754 }
4755
4756 const Section &user = sections.back();
4757 if (section_header(user.key, user.title, user.entries.size(), 0.f, {}, user.title)) {
4758 const ImVec2 origin = ImGui::GetCursorScreenPos();
4759 const int count = grid(origin, user.entries, true);
4760 ImGui::SetCursorScreenPos(tile_pos(origin, count));
4761 if (ImGui::Button("+##import_texture", ImVec2(tile, tile)))
4762 import_clicked = true;
4763 if (ImGui::IsItemHovered())
4764 panel_tooltip(_u8L("Import your own image as a height map. It is converted to the format the slicer "
4765 "bakes from and saved to your personal texture folder, kept separate from the "
4766 "textures shipped with OrcaSlicer."));
4767 end_grid(origin, count + 1);
4768 // Belongs to this section: it describes the import tile and the remove-on-hover tiles, both of
4769 // which live here, so it is hidden with them rather than left describing controls off-screen.
4770 ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + grid_w);
4771 ImGui::TextDisabled("%s", _u8L("PNG, JPG or BMP - converted to a height map on import. Hover one of yours to remove it.").c_str());
4772 ImGui::PopTextWrapPos();
4773 }
4774 if (loads_deferred)
4775 m_parent.request_extra_frame();
4776 // Done whether or not the current texture was drawn: one in a collapsed section stays where it is.
4778 ImGui::EndChild();
4780
4781 if (chosen || import_clicked || close_clicked || download_clicked)
4782 ImGui::CloseCurrentPopup();
4783 m_tooltip_anchor_min = prev_anchor_min;
4784 m_tooltip_anchor_max = prev_anchor_max;
4785 ImGui::EndPopup();
4786 // The OrcaCloud plugin hub filtered to resource textures; the pane's session handoff redirects here.
4787 if (download_clicked)
4788 wxGetApp().open_orca_cloud("/app/plugins/plugin-hub?types=resource-textures");
4789
4790 // Only once the loop over the library is done: importing, picking (which reorders the recently used)
4791 // and removing all change the library the loop was iterating.
4792 if (chosen)
4793 set_layer_texture(*layer, *chosen);
4794 else if (import_clicked)
4795 import_custom_texture(*layer);
4796 else if (to_remove) {
4797 MessageDialog dlg(nullptr,
4798 wxString::Format(_L("Remove \"%s\" from My textures?\n\nThe image file is deleted from your "
4799 "texture folder. Layers already using it keep it."),
4800 from_u8(to_remove->name)),
4801 _L("Remove texture"), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION);
4802 if (dlg.ShowModal() == wxID_YES) {
4803 std::string error;
4804 if (remove_user_texture(to_remove->path, error))
4805 m_library_textures.erase(to_remove->path); // its cached thumbnail and bytes go with it
4806 else
4807 show_error(nullptr, error);
4808 }
4809 // The modal dialog took the focus, which closes the popup; the user was choosing a texture and
4810 // still is, so bring it back.
4811 m_picker_open_request = true;
4812 }
4813}
float m_picker_scroll_target
Definition GLGizmoTextureDisplacement.hpp:511
void import_custom_texture(TextureDisplacementLayer &layer)
Definition GLGizmoTextureDisplacement.cpp:4336
float m_picker_scroll_applied
Definition GLGizmoTextureDisplacement.hpp:512
uint64_t m_library_generation
Definition GLGizmoTextureDisplacement.hpp:1148
constexpr int PICKER_LOADS_PER_FRAME
Definition GLGizmoTextureDisplacement.cpp:181
bool remove_user_texture(const std::string &path, std::string &error)
Definition TextureLibrary.cpp:405
@ Plugin
Definition TextureLibrary.hpp:15
@ Shipped
Definition TextureLibrary.hpp:15
@ User
Definition TextureLibrary.hpp:15
uint64_t texture_library_generation()
Definition TextureLibrary.cpp:351
BoundingBox inset(BoundingBox box, const Point &by)
Definition IMEXArrange.cpp:38
coord_t height(const BoundingBox &box)
Definition Arrange.cpp:1185
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
std::map< std::string, std::string > Section
Definition EmbossStyleManager.cpp:562
@ hovered
Definition GLGizmoEmboss.cpp:223
size_t rows(const T &raster)
Definition MarchingSquares.hpp:171

References _L, _u8L, Slic3r::GUI::ImGuiWrapper::COL_ORCA, Slic3r::GUI::GLGizmoBase::count, ease_wheel_scroll(), Slic3r::GUI::from_u8(), Slic3r::GUI::GLTexture::get_height(), Slic3r::GUI::GLTexture::get_id(), get_library_texture(), Slic3r::GUI::GLTexture::get_width(), Slic3r::grid(), import_custom_texture(), Slic3r::GUI::GLGizmoBase::m_imgui, m_library_generation, m_library_textures, Slic3r::GUI::GLGizmoBase::m_parent, m_picker_open_request, m_picker_reveal_selected, m_picker_scroll_applied, m_picker_scroll_target, m_picker_slot, m_tooltip_anchor_max, m_tooltip_anchor_min, panel_tooltip(), Slic3r::TextureDisplacementLayer::path, Slic3r::GUI::Plugin, Slic3r::GUI::remove_user_texture(), set_layer_texture(), Slic3r::GUI::Shipped, Slic3r::GUI::show_error(), Slic3r::GUI::DPIAware< P >::ShowModal(), Slic3r::TextureDisplacementLayer::slot, style, Slic3r::ModelVolume::texture_displacement_layers, Slic3r::GUI::texture_library(), Slic3r::GUI::texture_library_generation(), texture_volume(), Slic3r::GUI::GLGizmoTextureDisplacement::LibraryTexture::thumbnail, and Slic3r::GUI::User.

Here is the call graph for this function:

◆ render_tip_media()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_tip_media ( const std::vector< TipMedia > & media,
float width )
private
4162{
4163 // Collect what is actually there first: the layout divides the width between the illustrations, so
4164 // one that is missing has to drop out of the count rather than leave a gap.
4165 std::vector<std::pair<const AnimatedTexture *, const TipMedia *>> shown;
4166 std::string key;
4167 for (const TipMedia &m : media) {
4168 const AnimatedTexture *texture =
4169 m_tip_media.get((boost::filesystem::path(resources_dir()) / "images" / "texture_displacement" / m.file).string());
4170 if (texture != nullptr && texture->is_valid() && texture->width() > 0 && texture->height() > 0) {
4171 shown.emplace_back(texture, &m);
4172 key += m.file + "|";
4173 }
4174 }
4175 if (shown.empty())
4176 return;
4177
4178 // Each animation plays from its first frame when its tooltip opens: these are short loops showing
4179 // one setting going from one value to another, and joining one halfway through shows the user the
4180 // end of a change they never saw the start of.
4181 // Restart on a different tooltip, and on the same one opening again - which is what a gap in the
4182 // frames it was drawn on means, since it is drawn on every frame for as long as it is on screen.
4183 const int this_frame = ImGui::GetFrameCount();
4184 if (key != m_tip_media_key || this_frame != m_tip_media_frame + 1) {
4185 m_tip_media_key = key;
4187 }
4188 m_tip_media_frame = this_frame;
4189 const double time = tip_now() - m_tip_media_start;
4190
4191 const ImGuiStyle &style = ImGui::GetStyle();
4192 const float spacing = style.ItemSpacing.x;
4193 const float cell_w = (width - spacing * float(shown.size() - 1)) / float(shown.size());
4194 const ImU32 frame = ImGui::GetColorU32(ImGuiCol_Text, 0.25f);
4195
4196 // A single illustration would otherwise be drawn at the tooltip's full width, and a square one at
4197 // full width is a picture as tall as the text beside it is long. Capped and centred in its column.
4198 const float max_h = m_imgui->scaled(10.f);
4199
4200 ImGui::Spacing();
4201 for (size_t i = 0; i < shown.size(); ++i) {
4202 const AnimatedTexture *texture = shown[i].first;
4203 const TipMedia &entry = *shown[i].second;
4204 const float cell_x = ImGui::GetCursorPosX();
4205
4206 float w = cell_w;
4207 float h = std::round(cell_w * float(texture->height()) / float(texture->width()));
4208 if (h > max_h) {
4209 w = std::round(max_h * float(texture->width()) / float(texture->height()));
4210 h = max_h;
4211 }
4212
4213 ImGui::BeginGroup();
4214 const unsigned int id = texture->texture_id(time);
4215 ImGui::SetCursorPosX(cell_x + std::round(0.5f * (cell_w - w)));
4216 ImGui::Image((ImTextureID) (intptr_t) id, ImVec2(w, h));
4217 ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), frame, style.FrameRounding);
4218 // The canvas only redraws on demand, so an animation has to ask for its own frames. It asks for
4219 // them as fast as they come rather than pacing itself with a timer: which frame is drawn is
4220 // decided by the clock above, so a redraw that arrives early simply draws the same frame again,
4221 // and a guaranteed frame is worth more here than a frugal one that may never arrive.
4222 if (texture->is_animated())
4223 m_parent.request_extra_frame();
4224
4225 if (!entry.caption.empty()) {
4226 // Centred under its own illustration, and never wider than its column.
4227 const std::string caption = into_u8(entry.caption);
4228 const float text_w = ImGui::CalcTextSize(caption.c_str()).x;
4229 ImGui::SetCursorPosX(cell_x + std::max(0.f, (cell_w - text_w) * 0.5f));
4230 ImGui::PushTextWrapPos(cell_x + cell_w);
4231 ImGui::TextDisabled("%s", caption.c_str());
4232 ImGui::PopTextWrapPos();
4233 }
4234 ImGui::EndGroup();
4235
4236 if (i + 1 < shown.size())
4237 ImGui::SameLine(0.f, spacing);
4238 }
4239}
double m_tip_media_start
Definition GLGizmoTextureDisplacement.hpp:1281
AnimatedTextureCache m_tip_media
Definition GLGizmoTextureDisplacement.hpp:1276
std::string m_tip_media_key
Definition GLGizmoTextureDisplacement.hpp:1280
int m_tip_media_frame
Definition GLGizmoTextureDisplacement.hpp:1282
Definition GLGizmoTextureDisplacement.hpp:1254

References Slic3r::GUI::GLGizmoTextureDisplacement::TipMedia::caption, Slic3r::GUI::AnimatedTexture::height(), Slic3r::GUI::into_u8(), Slic3r::GUI::AnimatedTexture::is_animated(), Slic3r::GUI::AnimatedTexture::is_valid(), Slic3r::GUI::GLGizmoBase::m_imgui, Slic3r::GUI::GLGizmoBase::m_parent, m_tip_media, m_tip_media_frame, m_tip_media_key, m_tip_media_start, Slic3r::resources_dir(), style, Slic3r::GUI::AnimatedTexture::texture_id(), Slic3r::GUI::tip_now(), and Slic3r::GUI::AnimatedTexture::width().

Referenced by panel_tooltip().

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

◆ render_triangles() [1/2]

void Slic3r::GUI::GLGizmoPainterBase::render_triangles ( const Selection & selection,
const ModelVolume * skip = nullptr ) const
protectedvirtualinherited
117{
118 render_triangles(selection, skip == nullptr ? std::vector<const ModelVolume*>{} : std::vector<const ModelVolume*>{skip});
119}

References render_triangles().

Referenced by Slic3r::GUI::GLGizmoFdmSupports::render_painter_gizmo(), Slic3r::GUI::GLGizmoFuzzySkin::render_painter_gizmo(), Slic3r::GUI::GLGizmoMmuSegmentation::render_painter_gizmo(), Slic3r::GUI::GLGizmoSeam::render_painter_gizmo(), Slic3r::GUI::GLGizmoTextureDisplacement::render_painter_gizmo(), and render_triangles().

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

◆ render_triangles() [2/2]

void Slic3r::GUI::GLGizmoPainterBase::render_triangles ( const Selection & selection,
const std::vector< const ModelVolume * > & skip ) const
protectedinherited
122{
123 auto* shader = wxGetApp().get_shader("mm_gouraud");
124 if (!shader)
125 return;
126 shader->start_using();
127 ScopeGuard guard([shader]() { if (shader) shader->stop_using(); });
128
130 shader->set_uniform("clipping_plane", clp_data.clp_dataf);
131 shader->set_uniform("z_range", clp_data.z_range);
132
133 // BBS: to improve the random white pixel issue
135
136 const ModelObject* mo = m_c->selection_info()->model_object();
137 int mesh_id = -1;
138 for (const ModelVolume* mv : mo->volumes) {
139 if (!mv->is_model_part())
140 continue;
141
142 ++mesh_id;
143 if (std::find(skip.begin(), skip.end(), mv) != skip.end())
144 continue;
145
146 Transform3d trafo_matrix;
147 if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) {
148 trafo_matrix = mo->instances[selection.get_instance_idx()]->get_assemble_transformation().get_matrix() * mv->get_matrix();
149 trafo_matrix.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + mo->instances[selection.get_instance_idx()]->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0));
150 }
151 else {
152 trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix()* mv->get_matrix();
153 }
154
155 bool is_left_handed = trafo_matrix.matrix().determinant() < 0.;
156 if (is_left_handed)
158
159 const Camera& camera = wxGetApp().plater()->get_camera();
160 const Transform3d& view_matrix = camera.get_view_matrix();
161 shader->set_uniform("view_model_matrix", view_matrix * trafo_matrix);
162 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
163 const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose();
164 shader->set_uniform("view_normal_matrix", view_normal_matrix);
165
167 Matrix3f normal_matrix = static_cast<Matrix3f>(trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>());
168
169 // Compute up direction accounting for build plate tilt
170 Vec3f up_direction = get_tilt_up_direction();
171
172 shader->set_uniform("volume_world_matrix", trafo_matrix);
173 shader->set_uniform("volume_mirrored", is_left_handed);
174 shader->set_uniform("slope.actived", m_parent.is_using_slope());
175 shader->set_uniform("slope.volume_world_normal_matrix", normal_matrix);
176 shader->set_uniform("slope.normal_z", normal_z);
177 shader->set_uniform("slope.up_direction", up_direction);
178 m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix);
179
180 if (is_left_handed)
182 }
183}

References Slic3r::GUI::GLCanvas3D::CanvasAssembleView, Slic3r::cast(), Slic3r::Geometry::deg2rad(), Slic3r::GLVolume::explosion_ratio, get_clipping_plane_data(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::Geometry::Transformation::get_offset(), Slic3r::GUI::Camera::get_projection_matrix(), get_tilt_up_direction(), Slic3r::ModelVolume::get_transformation(), Slic3r::GUI::Camera::get_view_matrix(), GL_CCW, GL_CULL_FACE, GL_CW, glDisable, glFrontFace, glsafe, Slic3r::ModelObject::instances, Slic3r::GUI::is_left_handed(), Slic3r::ModelVolume::is_model_part(), Slic3r::GUI::GLGizmoBase::m_c, m_highlight_by_angle_threshold_deg, Slic3r::GUI::GLGizmoBase::m_imgui, Slic3r::GUI::GLGizmoBase::m_parent, m_triangle_selectors, and Slic3r::ModelObject::volumes.

Here is the call graph for this function:

◆ render_uvcheck_mesh()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_uvcheck_mesh ( )
private
2242{
2243 const ModelObject *mo = m_c->selection_info()->model_object();
2244 const ModelVolume *mv = texture_volume();
2245 if (mo == nullptr || mv == nullptr || !m_uvcheck_glmodel.is_initialized())
2246 return;
2247 const TextureDisplacementLayer *layer = active_layer();
2248 if (layer == nullptr)
2249 return;
2250 GLShaderProgram *shader = wxGetApp().get_shader("texture_displacement_uvcheck");
2251 if (shader == nullptr)
2252 return;
2253
2254 const Selection &selection = m_parent.get_selection();
2255 const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
2256 const Camera &camera = wxGetApp().plater()->get_camera();
2257
2258 shader->start_using();
2259 shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix);
2260 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
2261 shader->set_uniform("volume_world_matrix", trafo_matrix);
2262 const ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data();
2263 shader->set_uniform("clipping_plane", clp_data.clp_dataf);
2264 shader->set_uniform("z_range", clp_data.z_range);
2265 // The shader works on world-space normals (it projects the texture in world millimetres, as the
2266 // bake does), so the normal matrix is the view rotation alone - no model part.
2267 const Matrix3d view_normal_matrix = camera.get_view_matrix().matrix().block(0, 0, 3, 3);
2268 shader->set_uniform("view_normal_matrix", view_normal_matrix);
2269 shader->set_uniform("volume_mirrored", trafo_matrix.matrix().determinant() < 0.0);
2270 shader->set_uniform("tex_anchor", Vec3f(trafo_matrix.translation().cast<float>())); // see the preview shader
2271 shader->set_uniform("mode", m_uv_check_mode == UVCheckMode::Distortion ? 1 : 0);
2272 shader->set_uniform("checker_freq", 4.f); // squares per texture tile
2273 shader->set_uniform("tiling_scale", layer->tiling_scale);
2274 shader->set_uniform("rotation_rad", layer->rotation_deg * float(M_PI) / 180.f);
2275 shader->set_uniform("uv_offset", layer->offset);
2276 shader->set_uniform("use_vertex_uv", m_uvcheck_uses_vertex_uv);
2277 shader->set_uniform("projection_mode", m_uvcheck_projection_mode);
2278 shader->set_uniform("patch_center", m_uvcheck_patch_center);
2279 shader->set_uniform("patch_axis", m_uvcheck_patch_axis);
2280 // Was never uploaded, so the checker disagreed with the bake for any non-square height map.
2281 shader->set_uniform("tex_aspect", layer_texture_aspect(*layer));
2282
2283 // Coincident with the base surface, so pull it toward the camera to win the depth test.
2285 glsafe(::glPolygonOffset(-1.0f, -1.0f));
2286 m_uvcheck_glmodel.render();
2288 shader->stop_using();
2289}

References active_layer(), Slic3r::GUI::GLGizmoPainterBase::ClippingPlaneDataWrapper::clp_dataf, Distortion, Slic3r::GUI::GLGizmoPainterBase::get_clipping_plane_data(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::Camera::get_view_matrix(), GL_POLYGON_OFFSET_FILL, glDisable, glEnable, glPolygonOffset, glsafe, Slic3r::ModelObject::instances, layer_texture_aspect(), Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, M_PI, m_uv_check_mode, m_uvcheck_glmodel, m_uvcheck_patch_axis, m_uvcheck_patch_center, m_uvcheck_projection_mode, m_uvcheck_uses_vertex_uv, Slic3r::TextureDisplacementLayer::offset, Slic3r::TextureDisplacementLayer::rotation_deg, Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), texture_volume(), Slic3r::TextureDisplacementLayer::tiling_scale, and Slic3r::GUI::GLGizmoPainterBase::ClippingPlaneDataWrapper::z_range.

Referenced by render_painter_gizmo().

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

◆ render_wireframe_overlay()

void Slic3r::GUI::GLGizmoTextureDisplacement::render_wireframe_overlay ( )
private
2353{
2354 const ModelObject *mo = m_c->selection_info()->model_object();
2355 const ModelVolume *mv = texture_volume();
2356 if (mo == nullptr || mv == nullptr || !m_wireframe_overlay_glmodel.is_initialized())
2357 return;
2358 GLShaderProgram *shader = wxGetApp().get_shader("flat");
2359 if (shader == nullptr)
2360 return;
2361
2362 const Selection &selection = m_parent.get_selection();
2363 const Transform3d trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
2364 const Camera &camera = wxGetApp().plater()->get_camera();
2365
2366 shader->start_using();
2367 shader->set_uniform("view_model_matrix", camera.get_view_matrix() * trafo_matrix);
2368 shader->set_uniform("projection_matrix", camera.get_projection_matrix());
2369 // Pull the lines toward the camera so they sit on the surface rather than z-fighting into it.
2371 glsafe(::glPolygonOffset(-1.0f, -1.0f));
2372 m_wireframe_overlay_glmodel.set_color(ColorRGBA(1.0f, 1.0f, 1.0f, 0.6f)); // white, so it reads on any material
2375 shader->stop_using();
2376}

References Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_projection_matrix(), Slic3r::GUI::Camera::get_view_matrix(), GL_POLYGON_OFFSET_LINE, glDisable, glEnable, glPolygonOffset, glsafe, Slic3r::ModelObject::instances, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, m_wireframe_overlay_glmodel, Slic3r::GLShaderProgram::set_uniform(), Slic3r::GLShaderProgram::start_using(), Slic3r::GLShaderProgram::stop_using(), and texture_volume().

Referenced by render_painter_gizmo().

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

◆ resting_drops()

std::vector< float > Slic3r::GUI::GLGizmoTextureDisplacement::resting_drops ( const std::vector< ModelVolume * > & parts)
private
3856{
3857 const auto bottom = [](const ModelVolume &part) {
3858 return part.mesh().transformed_bounding_box(texture_displacement_volume_to_world(part)).min.z();
3859 };
3860 // Each part's bottom is measured the same way as the object's, so the lowest part's drop is exactly
3861 // 0 and a single-part object bakes against its own bottom as it always has.
3862 double object_bottom = std::numeric_limits<double>::max();
3863 for (const ModelVolume *part : texture_parts())
3864 object_bottom = std::min(object_bottom, bottom(*part));
3865 std::vector<float> out;
3866 out.reserve(parts.size());
3867 for (const ModelVolume *part : parts)
3868 out.push_back(float(bottom(*part) - object_bottom));
3869 return out;
3870}

References Slic3r::texture_displacement_volume_to_world(), and texture_parts().

Referenced by bake(), queue_preview_job(), and run_stage_debug().

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

◆ run_stage_debug()

void Slic3r::GUI::GLGizmoTextureDisplacement::run_stage_debug ( )
private
6740{
6741 ModelVolume *mv = texture_volume();
6743 return;
6744
6745 update_model_object(); // flush the active layer's in-progress strokes, as a real bake would
6746 if (!mv->is_texture_displacement_painted()) {
6747 show_error(nullptr, _u8L("Nothing is painted, so there are no bake stages to capture."));
6748 return;
6749 }
6750
6751 // The default (one-run) pipeline refines as part of the bake, and Pro mode has the user prepare the
6752 // mesh themselves with the controls above - in both cases there is no preparation to replay, and
6753 // running one anyway would show stages the Bake button would not.
6754 const bool run_prepare = !pro_mode() && !mv->texture_displacement_options.pipeline_v2;
6755
6756 TextureDisplacementPrepareParams params;
6757 if (run_prepare) {
6758 apply_standard_mode_presets(mv); // capture what the panel is showing, exactly as Bake does
6759 params.remesh_edge_mm = STD_REMESH_EDGE_MM;
6760 params.remesh_sharp_deg = STD_REMESH_SHARP_DEG;
6761 params.subdiv_target_mm = STD_SUBDIV_MAX_EDGE_MM;
6762 params.subdiv_detail_mm = STD_SUBDIV_DETAIL_MM;
6763 params.subdiv_min_edge_mm = STD_SUBDIV_MIN_EDGE_MM;
6764 params.subdiv_border_mm = STD_SUBDIV_BORDER_MM;
6765 params.subdiv_color_edge_mm = STD_SUBDIV_COLOR_MM;
6766 params.subdiv_feature = true;
6767 params.subdiv_added_triangles = m_subdivide_budget_k * 1000;
6768 params.cut_steps = true;
6769 }
6770
6771 // Free the previous run's meshes before the next one allocates its own: every stage is a full
6772 // copy of the mesh, so holding two runs at once doubles what is already the expensive part.
6774
6775 m_debug_in_progress = true;
6777 *mv, color_settings_for(*mv), params, prepare_texture_frame(*mv), resting_drops({mv}).front(), run_prepare,
6779 [this](std::vector<BakeStageSnapshot> stages) {
6780 m_debug_in_progress = false;
6781 if (!stages.empty()) {
6782 m_debug_stages = std::move(stages);
6783 // Land on the last stage: that is what a real bake would have committed, and the
6784 // question is usually what it looks like before stepping back to find where it went
6785 // wrong.
6786 show_debug_stage(int(m_debug_stages.size()) - 1);
6787 }
6788 m_parent.set_as_dirty();
6789 });
6790}
void queue_texture_displacement_debug(const ModelVolume &volume, const TextureColorSettings &color, const TextureDisplacementPrepareParams &prepare_params, const Transform3d &prepare_texture_frame, float resting_drop, bool run_prepare, bool check_topology, std::function< void(std::vector< BakeStageSnapshot >)> on_finished)
Definition TextureDisplacementDebugJob.cpp:108

References _u8L, apply_standard_mode_presets(), color_settings_for(), Slic3r::TextureDisplacementPrepareParams::cut_steps, exit_debug_view(), Slic3r::ModelVolume::is_texture_displacement_painted(), m_bake_in_progress, m_debug_check_topology, m_debug_in_progress, m_debug_stages, Slic3r::GUI::GLGizmoBase::m_parent, m_prepare_in_progress, m_subdivide_budget_k, Slic3r::TextureDisplacementOptions::pipeline_v2, prepare_texture_frame(), pro_mode(), Slic3r::GUI::queue_texture_displacement_debug(), Slic3r::TextureDisplacementPrepareParams::remesh_edge_mm, Slic3r::TextureDisplacementPrepareParams::remesh_sharp_deg, resting_drops(), show_debug_stage(), Slic3r::GUI::show_error(), Slic3r::GUI::STD_REMESH_EDGE_MM, Slic3r::GUI::STD_REMESH_SHARP_DEG, Slic3r::GUI::STD_SUBDIV_BORDER_MM, Slic3r::GUI::STD_SUBDIV_COLOR_MM, Slic3r::GUI::STD_SUBDIV_DETAIL_MM, Slic3r::GUI::STD_SUBDIV_MAX_EDGE_MM, Slic3r::GUI::STD_SUBDIV_MIN_EDGE_MM, Slic3r::TextureDisplacementPrepareParams::subdiv_added_triangles, Slic3r::TextureDisplacementPrepareParams::subdiv_border_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_color_edge_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_detail_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_feature, Slic3r::TextureDisplacementPrepareParams::subdiv_min_edge_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_target_mm, Slic3r::ModelVolume::texture_displacement_options, texture_volume(), and update_model_object().

Referenced by render_debug_stage_panel().

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

◆ run_uv_command()

void Slic3r::GUI::GLGizmoTextureDisplacement::run_uv_command ( int cmd,
float value )
private
2972{
2974 if (cmd == int(Command::PaneClosed)) {
2975 // Closed with the pane's own X: keep it closed until asked again, and upload the background afresh then.
2976 m_show_uv_editor = false;
2978 // The seam tool belongs to the pane: left on with the pane gone, every stroke on the model would
2979 // be swallowed as a seam click and nothing would paint.
2980 if (m_seam_edit_mode) {
2981 m_seam_edit_mode = false;
2982 m_seam_hover_edge = { -1, -1 };
2984 m_seam_hover_glmodel.reset();
2985 m_seam_path_anchor = -1;
2986 m_seam_anchor_glmodel.reset();
2988 }
2989 return;
2990 }
2991 if (cmd == int(Command::SetBackground)) {
2992 // Height goes back to whichever of Normal / Fast was showing; Checker and Distortion are views of their own.
2993 const int background = std::clamp(int(std::lround(value)), 0, 2);
2994 apply_view_mode(background == 1 ? 2 : background == 2 ? 3 : (m_use_shaded_preview ? 1 : 0));
2995 return;
2996 }
2997
2998 TextureDisplacementLayer *layer = active_layer();
2999 if (layer == nullptr)
3000 return;
3001
3002 if (cmd == int(UVEditorCanvas::Command::AverageScale)) {
3003 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Average island scale"), UndoRedo::SnapshotType::GizmoAction);
3004 average_island_scales(layer->islands);
3006 } else if (cmd == int(UVEditorCanvas::Command::CutSelectedIsland)) {
3007 UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas();
3008 const int chart = canvas != nullptr ? canvas->selected_island() : -1;
3009 if (chart < 0) {
3010 show_error(nullptr, _u8L("Select an island in the UV editor first."));
3011 return;
3012 }
3013 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Cut texture island"), UndoRedo::SnapshotType::GizmoAction);
3014 cut_island(*layer, chart);
3016 } else if (cmd == int(UVEditorCanvas::Command::JoinSelected)) {
3017 UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas();
3018 const int chart = canvas != nullptr ? canvas->selected_island() : -1;
3019 if (chart < 0) {
3020 show_error(nullptr, _u8L("Select an island in the UV editor first."));
3021 return;
3022 }
3023 // Join to whichever neighbouring island (one it shares an edge with) is currently placed
3024 // nearest - i.e. the one it was dragged up against.
3025 const Eigen::Matrix<float, 2, 3> sel_m = island_transform_matrix(chart, m_uv_editor_unwrap, layer->islands);
3026 const Vec2f sel_c = sel_m.block<2, 2>(0, 0) * m_uv_editor_unwrap.chart_centroid[size_t(chart)] + sel_m.col(2);
3027 int best_parent = -1;
3028 float best_d2 = std::numeric_limits<float>::max();
3029 TextureIsland best_place, cand;
3030 for (int p = 0; p < m_uv_editor_unwrap.chart_count; ++p) {
3031 if (p == chart)
3032 continue;
3033 if (!join_chart_placement(m_uv_editor_unwrap, layer->islands, chart, p, cand))
3034 continue; // not a neighbour
3035 const Eigen::Matrix<float, 2, 3> pm = island_transform_matrix(p, m_uv_editor_unwrap, layer->islands);
3036 const Vec2f pc = pm.block<2, 2>(0, 0) * m_uv_editor_unwrap.chart_centroid[size_t(p)] + pm.col(2);
3037 const float d2 = (pc - sel_c).squaredNorm();
3038 if (d2 < best_d2) { best_d2 = d2; best_parent = p; best_place = cand; }
3039 }
3040 if (best_parent < 0) {
3041 show_error(nullptr, _u8L("This island has no neighbour it shares an edge with."));
3042 return;
3043 }
3044 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Join texture island"), UndoRedo::SnapshotType::GizmoAction);
3045 if (layer->islands.size() <= size_t(chart))
3046 layer->islands.resize(size_t(chart) + 1);
3047 layer->islands[size_t(chart)] = best_place;
3048 // Record the join so the two (and anything already grouped with either) move together from now
3049 // on, not just visually snap once.
3050 join_island_groups(layer->island_groups, chart, best_parent, m_uv_editor_unwrap.chart_count);
3052 } else if (cmd == int(UVEditorCanvas::Command::UnjoinSelected)) {
3053 UVEditorCanvas *canvas = wxGetApp().plater()->get_uv_editor_canvas();
3054 const int chart = canvas != nullptr ? canvas->selected_island() : -1;
3055 if (chart < 0 || size_t(chart) >= layer->islands.size()) {
3056 show_error(nullptr, _u8L("Select an island in the UV editor first."));
3057 return;
3058 }
3059 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Unjoin texture island"), UndoRedo::SnapshotType::GizmoAction);
3060 layer->islands[size_t(chart)] = TextureIsland{}; // back to its own packed position
3061 // Break its join link too, so it stops moving with the others (the rest stay grouped).
3062 if (size_t(chart) < layer->island_groups.size())
3063 layer->island_groups[size_t(chart)] = chart;
3065 }
3066 else if (cmd == int(Command::PickTexture)) {
3067 // The same texture library the layer card opens; the panel renders it this frame.
3069 m_picker_open_request = true;
3071 } else if (cmd == int(Command::Unwrap)) {
3072 // The solve runs only on this - painting, the seam angle and seam marking never trigger it.
3073 m_uv_unwrap_pending = true;
3074 m_uv_apply_connected_net = true; // a genuine re-unwrap may relayout the islands
3075 m_show_uv_editor = true;
3077 } else if (cmd == int(Command::SetSeamAngle)) {
3078 const float angle = std::clamp(value, 5.f, 90.f);
3079 if (angle != layer->lscm_seam_angle_deg) {
3080 layer->lscm_seam_angle_deg = angle;
3081 rebuild_preview(); // the bake unwraps with it; this also marks the pane's unwrap stale
3082 }
3083 } else if (cmd == int(Command::SetConnectIslands)) {
3084 const bool connect = value != 0.f;
3085 if (connect != layer->auto_connect_islands) {
3086 layer->auto_connect_islands = connect;
3087 // Apply (or, when turned off, just stop re-applying) right away rather than waiting for the next re-unwrap.
3088 if (connect && !m_uv_editor_unwrap.empty()) {
3089 std::vector<TextureIsland> net = compute_connected_net(m_uv_editor_unwrap);
3090 if (net.size() == size_t(m_uv_editor_unwrap.chart_count)) {
3091 if (layer->islands.size() < net.size())
3092 layer->islands.resize(net.size());
3093 for (size_t i = 0; i < net.size(); ++i)
3094 layer->islands[i] = net[i];
3095 }
3096 }
3098 }
3099 } else if (cmd == int(Command::SetSelectMode)) {
3100 m_uv_select_mode = std::clamp(int(std::lround(value)), 0, 2); // the canvas has already switched
3101 } else if (cmd == int(Command::SetMarkSeams)) {
3102 m_seam_edit_mode = value != 0.f;
3103 if (m_seam_edit_mode)
3104 m_adjust_texture_mode = false; // the two click modes are mutually exclusive
3105 else {
3106 m_seam_hover_edge = { -1, -1 }; // drop the hover highlight when leaving the mode
3108 m_seam_hover_glmodel.reset();
3109 m_seam_path_anchor = -1;
3110 m_seam_anchor_glmodel.reset();
3111 }
3113 } else if (cmd == int(Command::SetSeamPath)) {
3114 m_seam_path_mode = value != 0.f;
3115 m_seam_path_anchor = -1;
3116 m_seam_hover_edge = { -1, -1 }; // hover target type changes with the mode
3118 m_seam_hover_glmodel.reset();
3119 m_seam_anchor_glmodel.reset();
3121 } else if (cmd == int(Command::ClearSeams)) {
3122 if (!layer->lscm_seam_edges.empty()) {
3123 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Clear texture seams"), UndoRedo::SnapshotType::GizmoAction);
3124 layer->lscm_seam_edges.clear();
3126 }
3127 } else if (cmd == int(Command::ClearUVEdits)) {
3128 if (!layer->lscm_uv_overrides.empty()) {
3129 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Clear texture UV edits"), UndoRedo::SnapshotType::GizmoAction);
3130 layer->lscm_uv_overrides.clear();
3131 m_uv_unwrap_pending = true; // re-solve so the pane drops the edited coords
3134 }
3135 }
3136 // FrameAll/ToggleSnap are handled inside the canvas; ProjectFromView is not wired yet. SelectionChanged
3137 // only asks for this frame: follow_island_highlight() and the overlay read the selection back themselves.
3138 m_parent.set_as_dirty();
3139}
UVBackground m_uv_editor_bg
Definition GLGizmoTextureDisplacement.hpp:1084
static void join_island_groups(std::vector< int > &groups, int a, int b, int chart_count)
Definition GLGizmoTextureDisplacement.cpp:1591
bool m_uv_apply_connected_net
Definition GLGizmoTextureDisplacement.hpp:1076
bool m_uv_unwrap_pending
Definition GLGizmoTextureDisplacement.hpp:1072
void cut_island(TextureDisplacementLayer &layer, int chart)
Definition GLGizmoTextureDisplacement.cpp:3296
void push_uv_pane_state()
Definition GLGizmoTextureDisplacement.cpp:2895
int m_uv_select_mode
Definition GLGizmoTextureDisplacement.hpp:671
@ None
Definition GLGizmoTextureDisplacement.hpp:1083
Command
Definition UVEditorCanvas.hpp:113
@ UnjoinSelected
Definition UVEditorCanvas.hpp:114
@ AverageScale
Definition UVEditorCanvas.hpp:114
@ CutSelectedIsland
Definition UVEditorCanvas.hpp:114
@ JoinSelected
Definition UVEditorCanvas.hpp:114
@ Command
Definition ActionRegistry.hpp:25
void average_island_scales(std::vector< TextureIsland > &islands)
Definition TextureDisplacement.cpp:1957
Eigen::Matrix< float, 2, 3 > island_transform_matrix(int chart, const PatchUnwrap &unwrap, const std::vector< TextureIsland > &islands)
Definition TextureDisplacement.cpp:1453
std::vector< TextureIsland > compute_connected_net(const PatchUnwrap &unwrap)
Definition TextureDisplacement.cpp:1673
bool join_chart_placement(const PatchUnwrap &unwrap, const std::vector< TextureIsland > &islands, int child, int parent, TextureIsland &out_child)
Definition TextureDisplacement.cpp:1535

References _u8L, active_layer(), Slic3r::angle(), apply_view_mode(), Slic3r::TextureDisplacementLayer::auto_connect_islands, Slic3r::average_island_scales(), Slic3r::GUI::UVEditorCanvas::AverageScale, Slic3r::GUI::Command, Slic3r::compute_connected_net(), cut_island(), Slic3r::GUI::UVEditorCanvas::CutSelectedIsland, Slic3r::UndoRedo::GizmoAction, Slic3r::TextureDisplacementLayer::island_groups, Slic3r::island_transform_matrix(), Slic3r::TextureDisplacementLayer::islands, Slic3r::join_chart_placement(), join_island_groups(), Slic3r::GUI::UVEditorCanvas::JoinSelected, Slic3r::TextureDisplacementLayer::lscm_seam_angle_deg, Slic3r::TextureDisplacementLayer::lscm_seam_edges, Slic3r::TextureDisplacementLayer::lscm_uv_overrides, m_active_layer_slot, m_adjust_texture_mode, Slic3r::GUI::GLGizmoBase::m_parent, m_picker_open_request, m_picker_reveal_selected, m_picker_slot, m_seam_anchor_glmodel, m_seam_edit_mode, m_seam_hover_edge, m_seam_hover_glmodel, m_seam_hover_vertex, m_seam_path_anchor, m_seam_path_mode, m_show_uv_editor, m_use_shaded_preview, m_uv_apply_connected_net, m_uv_editor_bg, m_uv_editor_unwrap, m_uv_select_mode, m_uv_unwrap_pending, None, Slic3r::GUI::plater(), push_uv_pane_state(), rebuild_preview(), Slic3r::GUI::UVEditorCanvas::selected_island(), Slic3r::GUI::show_error(), Slic3r::GUI::UVEditorCanvas::UnjoinSelected, and update_uv_editor().

Referenced by process_uv_commands().

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

◆ save()

void Slic3r::GUI::GLGizmoBase::save ( cereal::BinaryOutputArchive & ar) const
inlineinherited
171{ on_save(ar); }
virtual void on_save(cereal::BinaryOutputArchive &ar) const
Definition GLGizmoBase.hpp:242

References on_save().

Here is the call graph for this function:

◆ seam_edge_at()

std::pair< int, int > Slic3r::GUI::GLGizmoTextureDisplacement::seam_edge_at ( const Vec2d & mouse_pos) const
private
942{
943 const ModelVolume *mv = texture_volume();
944 const ModelObject *mo = m_c->selection_info()->model_object();
945 if (mv == nullptr || mo == nullptr)
946 return { -1, -1 };
947
948 const int idx = texture_volume_raycaster_index();
949 if (idx < 0)
950 return { -1, -1 };
951 const auto &raycasters = m_c->raycaster()->raycasters();
952
953 const Selection &selection = m_parent.get_selection();
954 const Transform3d trafo = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
955 const Camera &camera = wxGetApp().plater()->get_camera();
956
957 Vec3f hit = Vec3f::Zero(), normal = Vec3f::Zero();
958 size_t facet = 0;
959 if (!raycasters[size_t(idx)]->unproject_on_mesh(mouse_pos, trafo, camera, hit, normal,
960 m_c->object_clipper()->get_clipping_plane(), &facet))
961 return { -1, -1 };
962
963 const indexed_triangle_set &its = mv->mesh().its;
964 if (facet >= its.indices.size())
965 return { -1, -1 };
966 const stl_triangle_vertex_indices &tri = its.indices[facet];
967
968 // The facet edge nearest the hit point (point-to-segment distance in mesh space).
969 const auto seg_dist = [](const Vec3f &p, const Vec3f &a, const Vec3f &b) {
970 const Vec3f ab = b - a;
971 const float l2 = ab.squaredNorm();
972 const float t = (l2 > 1e-12f) ? std::clamp((p - a).dot(ab) / l2, 0.f, 1.f) : 0.f;
973 return (p - (a + ab * t)).norm();
974 };
975 int best_i = 0;
976 float best_d = std::numeric_limits<float>::max();
977 for (int i = 0; i < 3; ++i) {
978 const float d = seg_dist(hit, its.vertices[tri[i]], its.vertices[tri[(i + 1) % 3]]);
979 if (d < best_d) { best_d = d; best_i = i; }
980 }
981 const int a = tri[best_i], b = tri[(best_i + 1) % 3];
982 return { std::min(a, b), std::max(a, b) };
983}
T l2(const boost::geometry::model::d2::point_xy< T > &v)
Definition ExtrusionSimulator.cpp:177

References Slic3r::dot(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::ModelObject::instances, Slic3r::TriangleMesh::its, Slic3r::l2(), Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::ModelVolume::mesh(), Slic3r::normal(), texture_volume(), and texture_volume_raycaster_index().

Referenced by on_mouse_seam(), and toggle_seam_at().

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

◆ seam_vertex_at()

int Slic3r::GUI::GLGizmoTextureDisplacement::seam_vertex_at ( const Vec2d & mouse_pos) const
private
1004{
1005 const ModelVolume *mv = texture_volume();
1006 const ModelObject *mo = m_c->selection_info()->model_object();
1007 if (mv == nullptr || mo == nullptr)
1008 return -1;
1009 const int idx = texture_volume_raycaster_index();
1010 if (idx < 0)
1011 return -1;
1012 const auto &raycasters = m_c->raycaster()->raycasters();
1013
1014 const Selection &selection = m_parent.get_selection();
1015 const Transform3d trafo = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix();
1016 const Camera &camera = wxGetApp().plater()->get_camera();
1017
1018 Vec3f hit = Vec3f::Zero(), normal = Vec3f::Zero();
1019 size_t facet = 0;
1020 if (!raycasters[size_t(idx)]->unproject_on_mesh(mouse_pos, trafo, camera, hit, normal,
1021 m_c->object_clipper()->get_clipping_plane(), &facet))
1022 return -1;
1023 const indexed_triangle_set &its = mv->mesh().its;
1024 if (facet >= its.indices.size())
1025 return -1;
1026 const stl_triangle_vertex_indices &tri = its.indices[facet];
1027 int best = tri[0];
1028 float best_d = std::numeric_limits<float>::max();
1029 for (int i = 0; i < 3; ++i) {
1030 const float d = (hit - its.vertices[tri[i]]).squaredNorm();
1031 if (d < best_d) { best_d = d; best = tri[i]; }
1032 }
1033 return best;
1034}

References Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::ModelObject::instances, Slic3r::TriangleMesh::its, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::ModelVolume::mesh(), Slic3r::normal(), texture_volume(), and texture_volume_raycaster_index().

Referenced by on_mouse_seam().

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

◆ select_visible_faces()

int Slic3r::GUI::GLGizmoTextureDisplacement::select_visible_faces ( const std::array< float, 12 > * uv_clip = nullptr)
private
5253{
5254 const ModelVolume *owner = texture_volume();
5255 ModelObject *mo = m_c->selection_info()->model_object();
5256 // Every model part, at its selector's and its raycaster's index (see texture_parts()): what is in
5257 // view gets painted whichever part it belongs to, and one part can hide another.
5258 const std::vector<ModelVolume *> parts = texture_parts();
5259 const std::vector<const MeshRaycaster *> raycasters = m_c->raycaster()->raycasters();
5260 if (owner == nullptr || mo == nullptr || parts.size() > m_triangle_selectors.size() || parts.size() > raycasters.size())
5261 return 0;
5262
5263 const Selection &selection = m_parent.get_selection();
5264 const Transform3d instance = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix();
5265 const Transform3d owner_to_world = instance * owner->get_matrix();
5266 const Camera &camera = wxGetApp().plater()->get_camera();
5267
5268 // Pass 1 (cheap): drop back-facing triangles. Under perspective the view direction varies across
5269 // the model, so it is taken per triangle from the eye to the centroid; under an orthographic
5270 // camera get_position() is still a point on the view axis, so the same expression stays correct
5271 // in direction terms for everything actually on screen.
5272 const Vec3d eye = camera.get_position();
5273 const bool ortho = camera.get_type() == Camera::EType::Ortho;
5274 const Vec3d fwd = camera.get_dir_forward();
5275
5276 std::vector<Geometry::Transformation> trafos;
5277 std::vector<std::vector<Vec3f>> centroids(parts.size()); // world coords, what get_unobscured_idxs() expects
5278 std::vector<std::vector<unsigned>> front_facing(parts.size()); // parallel: which facet each centroid came from
5279 trafos.reserve(parts.size());
5280 for (size_t p = 0; p < parts.size(); ++p) {
5281 const ModelVolume &part = *parts[p];
5282 const indexed_triangle_set &its = part.mesh().its;
5283 trafos.emplace_back(instance * part.get_matrix());
5284 const Transform3d &to_world = trafos.back().get_matrix();
5285 // The projection frame maps the owner's local coordinates (see apply_projection_frame()), so
5286 // another part's centroids are brought into them before the clip test.
5287 const Transform3d to_owner = (&part == owner) ? Transform3d::Identity() : Transform3d(owner_to_world.inverse() * to_world);
5288
5289 // Normals transform by the inverse transpose, not by the matrix itself - with a non-uniform
5290 // scale the two differ, and using the wrong one flips the facing test on the scaled axes.
5291 const Matrix3d normal_matrix = to_world.matrix().block<3, 3>(0, 0).inverse().transpose();
5292
5293 centroids[p].reserve(its.indices.size() / 2);
5294 front_facing[p].reserve(its.indices.size() / 2);
5295 for (size_t f = 0; f < its.indices.size(); ++f) {
5296 const stl_triangle_vertex_indices &tri = its.indices[f];
5297 const Vec3d a = its.vertices[tri[0]].cast<double>();
5298 const Vec3d b = its.vertices[tri[1]].cast<double>();
5299 const Vec3d c = its.vertices[tri[2]].cast<double>();
5300 const Vec3d n_world = normal_matrix * (b - a).cross(c - a);
5301 if (n_world.squaredNorm() < 1e-20)
5302 continue; // degenerate triangle: no meaningful normal, so no meaningful facing test
5303 const Vec3d centroid_local = (a + b + c) / 3.0;
5304 const Vec3d centroid_world = to_world * centroid_local;
5305 const Vec3d view_dir = ortho ? fwd : Vec3d(centroid_world - eye);
5306 if (n_world.dot(view_dir) >= 0.0)
5307 continue; // facing away from the camera
5308
5309 // Clip to the projection frame before the raycast, not after: outside the frame the texture
5310 // samples to nothing anyway, so those facets would only be painted to no effect - and this
5311 // is also what keeps the ray queries proportional to the framed area instead of the model.
5312 if (uv_clip != nullptr) {
5313 Vec2f uv;
5314 if (!project_uv_projective(*uv_clip, (to_owner * centroid_local).cast<float>(), uv))
5315 continue; // behind the projector
5316 if (uv.x() < 0.f || uv.x() > 1.f || uv.y() < 0.f || uv.y() > 1.f)
5317 continue;
5318 }
5319 centroids[p].emplace_back(centroid_world.cast<float>());
5320 front_facing[p].push_back(unsigned(f));
5321 }
5322 }
5323
5324 // Pass 2 (the expensive one): a real ray query per surviving centroid, so geometry in front of a
5325 // front-facing triangle correctly hides it - the far inner wall of a cup is front-facing but not
5326 // visible. This is why the whole thing is click-driven rather than live. Each raycaster only sees
5327 // its own part's mesh, so a centroid has to be clear of every one of them; its own part goes first,
5328 // since that is what hides most of them.
5329 size_t visible = 0;
5330 {
5331 wxBusyCursor wait;
5332 const ClippingPlane *clipping_plane = m_c->object_clipper()->get_clipping_plane();
5333 for (size_t p = 0; p < parts.size(); ++p) {
5334 std::vector<Vec3f> &points = centroids[p];
5335 std::vector<unsigned> &facets = front_facing[p];
5336 const auto keep_unobscured_by = [&](size_t q) {
5337 const std::vector<unsigned> clear = raycasters[q]->get_unobscured_idxs(trafos[q], camera, points, clipping_plane);
5338 std::vector<Vec3f> kept_points;
5339 std::vector<unsigned> kept_facets;
5340 kept_points.reserve(clear.size());
5341 kept_facets.reserve(clear.size());
5342 for (unsigned i : clear) {
5343 kept_points.push_back(points[i]);
5344 kept_facets.push_back(facets[i]);
5345 }
5346 points = std::move(kept_points);
5347 facets = std::move(kept_facets);
5348 };
5349 if (!points.empty())
5350 keep_unobscured_by(p);
5351 for (size_t q = 0; q < parts.size() && !points.empty(); ++q)
5352 if (q != p)
5353 keep_unobscured_by(q);
5354 visible += facets.size();
5355 }
5356 }
5357 if (visible == 0)
5358 return 0;
5359
5360 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Select visible faces for texture displacement"),
5362
5363 // Replaces the layer's paint rather than adding to it, on every part including those nothing was
5364 // visible on: the checkbox means "project onto what I can see", so a second capture from a new
5365 // angle should not leave the previous angle painted.
5366 // TriangleSelectorGUI, not the TriangleSelector base: request_update_render_data() is declared on
5367 // the GUI subclass, so binding to the base here would drop it.
5368 for (size_t p = 0; p < parts.size(); ++p) {
5369 TriangleSelectorGUI &selector = *m_triangle_selectors[p];
5370 selector.reset();
5371 for (unsigned f : front_facing[p])
5372 selector.set_facet(int(f), EnforcerBlockerType::ENFORCER);
5373 selector.request_update_render_data(true);
5374 }
5375
5377 m_parent.set_as_dirty();
5378 return int(visible);
5379}
@ Ortho
Definition Camera.hpp:39

References _u8L, Slic3r::cast(), Slic3r::cross(), Slic3r::ENFORCER, Slic3r::f(), Slic3r::GUI::Camera::get_dir_forward(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::ModelVolume::get_matrix(), Slic3r::GUI::Camera::get_position(), Slic3r::GUI::Camera::get_type(), Slic3r::UndoRedo::GizmoAction, Slic3r::ModelObject::instances, Slic3r::TriangleMesh::its, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::GUI::GLGizmoPainterBase::m_triangle_selectors, Slic3r::ModelVolume::mesh(), Slic3r::GUI::Camera::Ortho, Slic3r::GUI::plater(), Slic3r::project_uv_projective(), Slic3r::GUI::TriangleSelectorGUI::request_update_render_data(), Slic3r::TriangleSelector::reset(), Slic3r::TriangleSelector::set_facet(), texture_parts(), texture_volume(), and update_model_object().

Referenced by apply_projection_frame().

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

◆ select_whole_model()

void Slic3r::GUI::GLGizmoTextureDisplacement::select_whole_model ( )
private
5382{
5383 ModelObject *mo = m_c->selection_info()->model_object();
5384 if (!mo)
5385 return;
5386
5387 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Select whole model for texture displacement"), UndoRedo::SnapshotType::GizmoAction);
5388
5389 int idx = -1;
5390 for (const ModelVolume *v : mo->volumes) {
5391 if (!v->is_model_part())
5392 continue;
5393 ++idx;
5394 const size_t facet_count = v->mesh().its.indices.size();
5395 for (size_t i = 0; i < facet_count; ++i)
5396 m_triangle_selectors[idx]->set_facet(int(i), EnforcerBlockerType::ENFORCER);
5397 m_triangle_selectors[idx]->request_update_render_data(true);
5398 }
5400 m_parent.set_as_dirty();
5401}

References _u8L, Slic3r::ENFORCER, Slic3r::UndoRedo::GizmoAction, Slic3r::ModelVolume::is_model_part(), Slic3r::TriangleMesh::its, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::GUI::GLGizmoPainterBase::m_triangle_selectors, Slic3r::ModelVolume::mesh(), Slic3r::GUI::plater(), update_model_object(), and Slic3r::ModelObject::volumes.

Referenced by on_render_input_window().

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

◆ set_active_layer()

void Slic3r::GUI::GLGizmoTextureDisplacement::set_active_layer ( int slot)
private
3993{
3994 if (slot == m_active_layer_slot)
3995 return;
3996 // Flush edits made while the previous layer was active before switching what the selectors
3997 // reflect - otherwise they would be silently lost.
3999 m_active_layer_slot = slot;
4002 // The on-canvas gizmo (if on) is anchored to whichever layer is active - keep it in sync
4003 // instead of leaving it pointing at the previous layer's (now stale) paint patch.
4006 // Refresh every preview/overlay (shaded, UV editor, seams, ...) for the newly active layer.
4008}

References m_active_layer_slot, m_adjust_texture_mode, rebuild_preview(), store_panel_state(), update_adjust_anchor(), update_from_model_object(), and update_model_object().

Referenced by add_texture_layer().

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

◆ set_common_data_pool()

void Slic3r::GUI::GLGizmoBase::set_common_data_pool ( CommonGizmosDataPool * ptr)
inlineinherited
193{ m_c = ptr; }

References m_c.

◆ set_dirty()

void Slic3r::GUI::GLGizmoBase::set_dirty ( )
protectedinherited
524 {
525 m_dirty = true;
526}
bool m_dirty
Definition GLGizmoBase.hpp:293

References m_dirty.

Referenced by Slic3r::GUI::GLGizmoCut3D::process_cut_line(), and Slic3r::GUI::GLGizmoSimplify::request_rerender().

Here is the caller graph for this function:

◆ set_hover_id()

void Slic3r::GUI::GLGizmoBase::set_hover_id ( int id)
inherited
348{
349 // do not change hover id during dragging
350 assert(!m_dragging);
351
352 // allow empty grabbers when not using grabbers but use hover_id - flatten, rotate
353// if (!m_grabbers.empty() && id >= (int) m_grabbers.size())
354// return;
355
356 m_hover_id = id;
358}
virtual void on_set_hover_id()
Definition GLGizmoBase.hpp:245

References m_dragging, m_hover_id, and on_set_hover_id().

Referenced by Slic3r::GUI::GLGizmoRotate3D::on_set_hover_id().

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

◆ set_icon_filename()

void Slic3r::GUI::GLGizmoBase::set_icon_filename ( const std::string & filename)
inherited
343 {
344 m_icon_filename = filename;
345}

References m_icon_filename.

◆ set_layer_texture()

void Slic3r::GUI::GLGizmoTextureDisplacement::set_layer_texture ( TextureDisplacementLayer & layer,
const TextureLibraryEntry & entry )
private
4314{
4315 const LibraryTexture *tex = get_library_texture(entry.path);
4316 if (tex == nullptr) {
4317 show_error(nullptr, _u8L("Could not load the selected texture."));
4318 return;
4319 }
4320
4321 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Change texture displacement texture"), UndoRedo::SnapshotType::GizmoAction);
4322 layer.name = entry.name;
4323 layer.path = entry.path;
4324 // Handing over the library's own buffer (rather than a copy) is what lets decode_height_texture()
4325 // - whose cache is keyed by exactly this pointer - hit straight away instead of re-decoding
4326 // the PNG the first time the layer is previewed or baked.
4327 layer.image_data = tex->image_data;
4328 // The user's own textures are listed most recently used first.
4329 if (entry.source == TextureSource::User)
4330 touch_user_texture(entry.path);
4331
4333 m_parent.set_as_dirty();
4334}
void touch_user_texture(const std::string &path)
Definition TextureLibrary.cpp:392

References _u8L, get_library_texture(), Slic3r::UndoRedo::GizmoAction, Slic3r::GUI::GLGizmoTextureDisplacement::LibraryTexture::image_data, Slic3r::TextureDisplacementLayer::image_data, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::GUI::TextureLibraryEntry::name, Slic3r::TextureDisplacementLayer::name, Slic3r::GUI::TextureLibraryEntry::path, Slic3r::TextureDisplacementLayer::path, Slic3r::GUI::plater(), rebuild_preview(), Slic3r::GUI::show_error(), Slic3r::GUI::TextureLibraryEntry::source, Slic3r::GUI::touch_user_texture(), and Slic3r::GUI::User.

Referenced by commit_image_adjust(), import_custom_texture(), and render_texture_library_popup().

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

◆ set_state()

void Slic3r::GUI::GLGizmoBase::set_state ( EState state)
inlineinherited
176{ m_state = state; on_set_state(); }
virtual void on_set_state()
Definition GLGizmoBase.hpp:244

References m_state, and on_set_state().

Referenced by Slic3r::GUI::GLGizmosManager::activate_gizmo().

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

◆ shaded_preview_ready()

bool Slic3r::GUI::GLGizmoTextureDisplacement::shaded_preview_ready ( ) const
private
1778{
1779 // Mirrors render_shaded_preview_mesh()'s own preconditions. Kept as a separate query because the
1780 // caller has to know whether the shaded pass will draw *before* it hides the real volume for it.
1781 if (m_c->selection_info() == nullptr || m_c->selection_info()->model_object() == nullptr)
1782 return false;
1783 if (texture_volume() == nullptr)
1784 return false;
1785 const TextureDisplacementLayer *layer = active_layer();
1786 if (layer == nullptr || layer->empty())
1787 return false;
1788 // The same texture render_shaded_preview_mesh() will bind, not the panel thumbnail - the two are
1789 // separate caches and either can fail on its own.
1790 const GLTexture *tex = const_cast<GLGizmoTextureDisplacement *>(this)->get_layer_height_texture(*layer);
1791 if (tex == nullptr || tex->get_width() <= 0 || tex->get_height() <= 0)
1792 return false;
1793 return wxGetApp().get_shader("texture_displacement_shaded") != nullptr;
1794}

References GLGizmoTextureDisplacement(), active_layer(), Slic3r::TextureDisplacementLayer::empty(), Slic3r::GUI::GLTexture::get_height(), get_layer_height_texture(), Slic3r::GUI::GLTexture::get_width(), Slic3r::GUI::GLGizmoBase::m_c, and texture_volume().

Referenced by render_painter_gizmo().

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

◆ shortcut()

std::optional< Shortcut > Slic3r::GUI::GLGizmoBase::shortcut ( ) const
inlineinherited
178{ return m_shortcut; }

References m_shortcut.

Referenced by Slic3r::GUI::GLGizmoFdmSupports::on_tool_shortcut(), Slic3r::GUI::GLGizmoMmuSegmentation::on_tool_shortcut(), Slic3r::GUI::GLGizmoPainterBase::on_tool_shortcut(), and Slic3r::GUI::GLGizmoSeam::on_tool_shortcut().

Here is the caller graph for this function:

◆ show_debug_stage()

void Slic3r::GUI::GLGizmoTextureDisplacement::show_debug_stage ( int index)
private
6793{
6794 if (index < 0 || size_t(index) >= m_debug_stages.size())
6795 return;
6796 const BakeStageSnapshot &stage = m_debug_stages[size_t(index)];
6797 if (stage.mesh.empty())
6798 return; // over the memory cap: its counts are still listed, there is just nothing to draw
6799
6800 m_debug_stage = index;
6801 // Drawn through the ordinary true-displacement preview, so the Fast view - a shader trick over
6802 // the base mesh that never draws m_preview_glmodel - has to be left first.
6803 m_use_shaded_preview = false;
6804 m_preview_color_runs.clear();
6805 m_preview_glmodel.reset();
6806 // The stage is a capture of one part's run, so the other parts go back to being drawn as themselves
6807 // rather than keeping a live preview the captured stages say nothing about.
6808 m_part_previews.clear();
6809
6810 indexed_triangle_set its;
6811 its.vertices = stage.mesh.vertices;
6812 its.indices = stage.mesh.indices;
6813 m_preview_glmodel.init_from(its);
6815 // Kept so the wireframe overlay draws this stage's edges rather than the base mesh's - which is
6816 // most of the point, since what a refinement stage did is a change in the edges, not the surface.
6817 m_preview_its = std::move(its);
6819 m_parent.set_as_dirty();
6820}

References Slic3r::BakeStageMesh::empty(), Slic3r::BakeStageMesh::indices, m_debug_stage, m_debug_stages, Slic3r::GUI::GLGizmoBase::m_parent, m_part_previews, m_preview_color_runs, m_preview_glmodel, m_preview_its, m_use_shaded_preview, Slic3r::BakeStageSnapshot::mesh, Slic3r::GLVolume::NEUTRAL_COLOR, refresh_wireframe(), and Slic3r::BakeStageMesh::vertices.

Referenced by render_debug_stage_panel(), and run_stage_debug().

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

◆ show_projector()

void Slic3r::GUI::GLGizmoTextureDisplacement::show_projector ( bool show)
private
3166{
3167 if (!show) {
3168 if (m_projector_frame != nullptr)
3169 m_projector_frame->Hide();
3170 return;
3171 }
3172
3173 if (m_projector_frame == nullptr) {
3174 // Parented to the main frame, not to Plater: Plater is a wxPanel, and wxFRAME_FLOAT_ON_PARENT
3175 // wants a real top-level window to float above.
3176 m_projector_frame = new TextureProjectorFrame(wxGetApp().mainframe);
3177 m_projector_tex_source = nullptr; // fresh window, nothing uploaded into it yet
3180
3181 // Opened centred over the 3D canvas, at about half its size: the frame is meant to be
3182 // dragged onto part of the model, so starting somewhere on top of it beats the OS's default
3183 // cascade position, which is often off over the sidebar.
3184 if (const wxGLCanvas *cnv = m_parent.get_wxglcanvas(); cnv != nullptr) {
3185 const wxRect area = cnv->GetScreenRect();
3186 const wxSize size(std::max(160, area.width / 2), std::max(160, area.height / 2));
3187 m_projector_frame->SetSize(wxRect(area.GetTopLeft() + wxPoint((area.width - size.x) / 2,
3188 (area.height - size.y) / 2),
3189 size));
3190 }
3191 }
3192
3193 m_projector_frame->Show();
3194 m_projector_frame->Raise();
3196}

References Slic3r::area(), Slic3r::GUI::GLGizmoBase::m_parent, m_projector_frame, m_projector_opacity, m_projector_tex_smoothing, m_projector_tex_source, and update_projector().

Here is the call graph for this function:

◆ smooth_model()

void Slic3r::GUI::GLGizmoTextureDisplacement::smooth_model ( )
private
6311{
6312 ModelVolume *mv = texture_volume();
6313 ModelObject *mo = m_c->selection_info()->model_object();
6314 if (mv == nullptr || mo == nullptr)
6315 return;
6316 const TextureDisplacementOptions &opts = mv->texture_displacement_options;
6317 if (opts.smooth_strength <= 0.f || opts.smooth_iterations <= 0)
6318 return;
6319
6320 update_model_object(); // flush any in-progress stroke, so the painted region below is current
6321
6322 // Movable = the vertices of the painted triangles, so the first ring of purely unpainted vertices
6323 // outside them stays put and anchors the result. Restricted rather than whole-model on purpose:
6324 // this runs on geometry that has already been baked, and relaxing the whole thing would quietly
6325 // round off every unrelated feature on the part. With "Ignore outer ring" the patch's own rim is
6326 // held as well - see TextureDisplacementOptions::smooth_skip_border.
6327 const indexed_triangle_set &its = mv->mesh().its;
6328 std::vector<uint8_t> painted_face(its.indices.size(), 0);
6329 bool any = false;
6330 for (int slot = 0; slot < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++slot)
6331 for (const TriangleSelector::TriangleBitStreamMapping &m : mv->texture_displacement_facet(slot).get_data().triangles_to_split)
6332 if (size_t(m.triangle_idx) < its.indices.size()) {
6333 painted_face[size_t(m.triangle_idx)] = 1;
6334 any = true;
6335 }
6336 if (!any) {
6337 show_error(nullptr, _u8L("Paint the area you want to smooth first - smoothing only touches the "
6338 "painted part of the model."));
6339 return;
6340 }
6341 std::vector<uint8_t> movable(its.vertices.size(), 0);
6342 for (size_t i = 0; i < its.indices.size(); ++i)
6343 if (painted_face[i])
6344 for (int k = 0; k < 3; ++k)
6345 movable[size_t(its.indices[i][k])] = 1;
6346 if (opts.smooth_skip_border)
6347 for (size_t i = 0; i < its.indices.size(); ++i)
6348 if (!painted_face[i])
6349 for (int k = 0; k < 3; ++k)
6350 movable[size_t(its.indices[i][k])] = 0; // also used by unpainted geometry -> the rim
6351 if (std::none_of(movable.begin(), movable.end(), [](uint8_t m) { return m != 0; })) {
6352 // Every painted vertex is on the patch's rim, so "Ignore outer ring" leaves nothing to move.
6353 show_error(nullptr, _u8L("Nothing to smooth - the painted area is only one triangle deep, so with "
6354 "\"Ignore outer ring\" on there are no interior vertices to relax."));
6355 return;
6356 }
6357
6358 indexed_triangle_set smoothed = its;
6359 {
6360 wxBusyCursor wait;
6361 smooth_mesh_vertices(smoothed, movable, opts.smooth_strength, opts.smooth_iterations);
6362 }
6363
6364 Plater *plater = wxGetApp().plater();
6365 Plater::TakeSnapshot snapshot(plater, _u8L("Smooth texture displacement"), UndoRedo::SnapshotType::GizmoAction);
6366
6367 // No save/restore-painting dance here, unlike subdivide and remesh: smoothing only moves vertices,
6368 // it does not touch the triangle list, so every paint channel still refers to exactly the triangles
6369 // it did before. set_mesh() clears the extra facets, so this saves and puts back the
6370 // texture-displacement masks verbatim - no remap needed, and none of them is lost.
6371 std::optional<TriangleSelector::SavedPainting> saved_painting = mv->save_painting();
6372 std::array<TriangleSelector::TriangleSplittingData, TEXTURE_DISPLACEMENT_MAX_LAYERS> saved_texture;
6373 for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i)
6374 saved_texture[size_t(i)] = mv->texture_displacement_facet(i).get_data();
6375
6376 mv->set_mesh(TriangleMesh(std::move(smoothed)));
6377 mv->set_new_unique_id();
6378 mv->calculate_convex_hull();
6379 mv->restore_painting(saved_painting);
6380 for (int i = 0; i < int(TEXTURE_DISPLACEMENT_MAX_LAYERS); ++i)
6381 mv->texture_displacement_facet(i).set_data(std::move(saved_texture[size_t(i)]));
6382
6383 if (ObjectList *obj_list = wxGetApp().obj_list()) {
6384 const ModelObjectPtrs &objs = plater->model().objects;
6385 auto it = std::find(objs.begin(), objs.end(), mo);
6386 if (it != objs.end())
6387 obj_list->update_info_items(size_t(it - objs.begin()));
6388 }
6389 plater->changed_object(*mo);
6391 m_parent.set_as_dirty();
6392}
void update_info_items(size_t obj_idx, wxDataViewItemArray *selections=nullptr, bool added_object=false, bool color_mode_changed=false)
Definition GUI_ObjectList.cpp:4039
void changed_object(ModelObject &object)
Definition Plater.cpp:22227
const Model & model() const
Definition Plater.cpp:16047
ModelObjectPtrs objects
Definition Model.hpp:1702
static ObjectList * obj_list()
Definition GUI_Factories.cpp:405
@ ObjectList
Definition Shortcuts.hpp:21
@ Plater
Definition Shortcuts.hpp:21
void smooth_mesh_vertices(indexed_triangle_set &mesh, const std::vector< uint8_t > &movable, float strength, int iterations, const DisplacementProgressFn &on_pass)
Definition TextureDisplacement.cpp:3615
std::vector< ModelObject * > ModelObjectPtrs
Definition CutUtils.hpp:13

References _u8L, Slic3r::ModelVolume::calculate_convex_hull(), Slic3r::FacetsAnnotation::get_data(), Slic3r::UndoRedo::GizmoAction, Slic3r::TriangleMesh::its, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::ModelVolume::mesh(), Slic3r::GUI::obj_list(), Slic3r::GUI::plater(), Slic3r::ModelVolume::restore_painting(), Slic3r::ModelVolume::save_painting(), Slic3r::FacetsAnnotation::set_data(), Slic3r::ModelVolume::set_mesh(), Slic3r::ModelVolume::set_new_unique_id(), Slic3r::GUI::show_error(), Slic3r::TextureDisplacementOptions::smooth_iterations, Slic3r::smooth_mesh_vertices(), Slic3r::TextureDisplacementOptions::smooth_skip_border, Slic3r::TextureDisplacementOptions::smooth_strength, Slic3r::ModelVolume::texture_displacement_facet(), Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS, Slic3r::ModelVolume::texture_displacement_options, texture_volume(), Slic3r::TriangleSelector::TriangleSplittingData::triangles_to_split, update_from_model_object(), and update_model_object().

Here is the call graph for this function:

◆ store_panel_state()

void Slic3r::GUI::GLGizmoTextureDisplacement::store_panel_state ( )
private
3985{
3986 if (ModelVolume *mv = texture_volume(); mv != nullptr) {
3987 mv->texture_displacement_options.active_slot = m_active_layer_slot;
3988 mv->texture_displacement_options.panel_mode = m_panel_mode;
3989 }
3990}

References m_active_layer_slot, m_panel_mode, and texture_volume().

Referenced by on_render_input_window(), set_active_layer(), and update_model_object().

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

◆ subdivide_model()

void Slic3r::GUI::GLGizmoTextureDisplacement::subdivide_model ( )
private
5404{
5405 ModelObject *mo = m_c->selection_info()->model_object();
5406 // Every model part, like the remesh: this is a plain mesh operation on the object, and a part left
5407 // coarse is a part no height map can raise relief on.
5408 const std::vector<ModelVolume *> parts = texture_parts();
5409 if (mo == nullptr || parts.empty() || m_subdivide_count < 1)
5410 return; // 0 passes means "no subdivision" - don't take a snapshot for a no-op
5411
5412 Plater *plater = wxGetApp().plater();
5413 Plater::TakeSnapshot snapshot(plater, _u8L("Subdivide model for texture displacement"), UndoRedo::SnapshotType::GizmoAction);
5414
5415 for (ModelVolume *mv : parts) {
5416 // Same save/replace/restore-painting dance GLGizmoSimplify uses when it re-tessellates a
5417 // volume's mesh: supported/seam/mmu/fuzzy-skin masks get remapped onto the new triangles,
5418 // texture-displacement doesn't (no remap support for it yet) and is dropped instead of being
5419 // left referring to triangle indices that no longer mean the same thing.
5420 std::optional<TriangleSelector::SavedPainting> saved_painting = mv->save_painting();
5421
5422 // max_edge_length 0 means "no triangle is ever small enough", so every non-degenerate edge is
5423 // split on each of the m_subdivide_count passes - i.e. a plain "subdivide the whole mesh N times".
5424 TriangleMesh new_mesh(subdivide_mesh_uniform(mv->mesh().its, 0.f, m_subdivide_count));
5425 mv->set_mesh(std::move(new_mesh));
5426 mv->set_new_unique_id();
5427 mv->calculate_convex_hull();
5428 mv->restore_painting(saved_painting);
5429 }
5430
5431 if (ObjectList *obj_list = wxGetApp().obj_list()) {
5432 const ModelObjectPtrs &objs = plater->model().objects;
5433 auto it = std::find(objs.begin(), objs.end(), mo);
5434 if (it != objs.end())
5435 obj_list->update_info_items(size_t(it - objs.begin()));
5436 }
5437
5438 plater->changed_object(*mo);
5439 update_from_model_object(false); // reload selectors/preview against the new mesh + cleared paint
5440 m_parent.set_as_dirty();
5441}

References _u8L, Slic3r::UndoRedo::GizmoAction, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, m_subdivide_count, Slic3r::GUI::obj_list(), Slic3r::GUI::plater(), Slic3r::subdivide_mesh_uniform(), texture_parts(), and update_from_model_object().

Here is the call graph for this function:

◆ subdivide_model_adaptive()

void Slic3r::GUI::GLGizmoTextureDisplacement::subdivide_model_adaptive ( )
private
6284{
6285 ModelVolume *mv = texture_volume();
6286 if (mv == nullptr || m_subdivide_target_mm <= 0.f)
6287 return;
6288
6289 update_model_object(); // flush any in-progress stroke into the committed masks first
6290
6291 if (!any_part_painted()) {
6292 show_error(nullptr, _u8L("Paint the area you want to subdivide first - adaptive subdivision only "
6293 "refines where you have painted."));
6294 return;
6295 }
6296
6297 TextureDisplacementPrepareParams params;
6298 params.subdiv_target_mm = m_subdivide_target_mm;
6299 params.subdiv_detail_mm = m_subdivide_detail_mm;
6300 params.subdiv_min_edge_mm = m_subdivide_min_edge_mm;
6301 params.subdiv_border_mm = m_subdivide_border_mm;
6302 params.subdiv_color_edge_mm = m_subdivide_color_mm;
6303 params.subdiv_feature = m_subdivide_feature;
6304 params.subdiv_added_triangles = m_subdivide_budget_k * 1000;
6305 queue_prepare(params, _u8L("Adaptive subdivide for texture displacement"), /* then_bake */ false,
6306 _u8L("Nothing to subdivide - the painted area already meets the target edge length and "
6307 "detail tolerance, or the triangle budget is already used up."));
6308}

References _u8L, any_part_painted(), m_subdivide_border_mm, m_subdivide_budget_k, m_subdivide_color_mm, m_subdivide_detail_mm, m_subdivide_feature, m_subdivide_min_edge_mm, m_subdivide_target_mm, queue_prepare(), Slic3r::GUI::show_error(), Slic3r::TextureDisplacementPrepareParams::subdiv_added_triangles, Slic3r::TextureDisplacementPrepareParams::subdiv_border_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_color_edge_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_detail_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_feature, Slic3r::TextureDisplacementPrepareParams::subdiv_min_edge_mm, Slic3r::TextureDisplacementPrepareParams::subdiv_target_mm, texture_volume(), and update_model_object().

Here is the call graph for this function:

◆ swap_layer_slots()

void Slic3r::GUI::GLGizmoTextureDisplacement::swap_layer_slots ( int slot_a,
int slot_b )
private
5180{
5181 ModelObject *mo = m_c->selection_info()->model_object();
5182 ModelVolume *mv = texture_volume();
5183 const int n = int(TEXTURE_DISPLACEMENT_MAX_LAYERS);
5184 if (mo == nullptr || mv == nullptr || slot_a == slot_b || slot_a < 0 || slot_b < 0 || slot_a >= n || slot_b >= n)
5185 return;
5186
5187 // Every model part, not only the texture volume: the selectors paint the active slot on all of them.
5188 for (ModelVolume *v : mo->volumes) {
5189 if (!v->is_model_part())
5190 continue;
5191 TriangleSelector::TriangleSplittingData data_a = v->texture_displacement_facet(slot_a).get_data();
5192 TriangleSelector::TriangleSplittingData data_b = v->texture_displacement_facet(slot_b).get_data();
5193 v->texture_displacement_facet(slot_a).set_data(std::move(data_b));
5194 v->texture_displacement_facet(slot_b).set_data(std::move(data_a));
5195 }
5196 for (TextureDisplacementLayer &l : mv->texture_displacement_layers) {
5197 if (l.slot == slot_a)
5198 l.slot = slot_b;
5199 else if (l.slot == slot_b)
5200 l.slot = slot_a;
5201 }
5202
5203 const size_t a = size_t(slot_a), b = size_t(slot_b);
5204 std::swap(m_thumbnails[a], m_thumbnails[b]);
5205 std::swap(m_thumbnail_source[a], m_thumbnail_source[b]);
5207 std::swap(m_layer_expanded[a], m_layer_expanded[b]);
5208 if (m_active_layer_slot == slot_a)
5209 m_active_layer_slot = slot_b;
5210 else if (m_active_layer_slot == slot_b)
5211 m_active_layer_slot = slot_a;
5212 if (m_picker_slot == slot_a)
5213 m_picker_slot = slot_b;
5214 else if (m_picker_slot == slot_b)
5215 m_picker_slot = slot_a;
5216}

References Slic3r::FacetsAnnotation::get_data(), Slic3r::ModelVolume::is_model_part(), m_active_layer_slot, Slic3r::GUI::GLGizmoBase::m_c, m_layer_expanded, m_picker_slot, m_thumbnail_smoothing, m_thumbnail_source, m_thumbnails, Slic3r::FacetsAnnotation::set_data(), Slic3r::TextureDisplacementLayer::slot, Slic3r::ModelVolume::texture_displacement_facet(), Slic3r::ModelVolume::texture_displacement_layers, Slic3r::TEXTURE_DISPLACEMENT_MAX_LAYERS, texture_volume(), and Slic3r::ModelObject::volumes.

Referenced by move_texture_layer().

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

◆ texture_anchor()

Vec3d Slic3r::GUI::GLGizmoTextureDisplacement::texture_anchor ( ) const
private
3850{
3851 const ModelVolume *owner = texture_volume();
3852 return owner != nullptr ? Vec3d(texture_displacement_volume_to_world(*owner).translation()) : Vec3d::Zero();
3853}

References Slic3r::texture_displacement_volume_to_world(), and texture_volume().

Referenced by bake(), prepare_texture_frame(), and queue_preview_job().

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

◆ texture_parts()

std::vector< ModelVolume * > Slic3r::GUI::GLGizmoTextureDisplacement::texture_parts ( )
private
3804{
3805 std::vector<ModelVolume *> out;
3806 ModelObject *mo = m_c->selection_info()->model_object();
3807 if (mo == nullptr)
3808 return out;
3809 for (ModelVolume *mv : mo->volumes)
3810 if (mv->is_model_part())
3811 out.push_back(mv);
3812 return out;
3813}

References Slic3r::ModelVolume::is_model_part(), Slic3r::GUI::GLGizmoBase::m_c, and Slic3r::ModelObject::volumes.

Referenced by any_part_painted(), painted_parts(), prepare_texture_frame(), queue_prepare(), remove_texture_layer(), resting_drops(), select_visible_faces(), and subdivide_model().

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

◆ texture_volume() [1/2]

ModelVolume * Slic3r::GUI::GLGizmoTextureDisplacement::texture_volume ( )
private
3788{
3789 ModelObject *mo = m_c->selection_info()->model_object();
3790 if (!mo)
3791 return nullptr;
3792 for (ModelVolume *mv : mo->volumes)
3793 if (mv->is_model_part())
3794 return mv;
3795 return nullptr;
3796}

References Slic3r::ModelVolume::is_model_part(), Slic3r::GUI::GLGizmoBase::m_c, and Slic3r::ModelObject::volumes.

Referenced by active_layer(), add_texture_layer(), adjust_plane_point(), apply_projection_frame(), bake(), bake_standard(), cached_palette(), capture_view_projection(), cut_island(), layer_projection_frame(), layers_for_part(), mark_seam_path(), model_uv_corners(), move_texture_layer(), on_mouse_adjust_texture(), on_render_input_window(), open_image_adjust(), patch_in_world(), push_uv_pane_state(), queue_prepare(), queue_preview_job(), rebuild_island_overlay(), rebuild_other_paint_overlay(), rebuild_paint_overlay(), rebuild_painted_colors(), rebuild_preview(), rebuild_seam_anchor_overlay(), rebuild_seam_hover_overlay(), rebuild_seam_overlay(), rebuild_shaded_preview_mesh(), rebuild_subdivide_preview(), rebuild_uvcheck_mesh(), rebuild_wireframe_overlay(), remesh_model(), remove_texture_layer(), render_adjust_texture_gizmo(), render_image_adjust_window(), render_island_overlay(), render_paint_overlay(), render_painted_colors(), render_painter_gizmo(), render_preview_mesh(), render_seam_overlay(), render_shaded_preview_mesh(), render_subdivide_preview(), render_texture_library_popup(), render_uvcheck_mesh(), render_wireframe_overlay(), run_stage_debug(), seam_edge_at(), seam_vertex_at(), select_visible_faces(), shaded_preview_ready(), smooth_model(), store_panel_state(), subdivide_model_adaptive(), swap_layer_slots(), texture_anchor(), texture_volume(), texture_volume_raycaster_index(), update_adjust_anchor(), update_from_model_object(), and update_uv_editor().

Here is the call graph for this function:

◆ texture_volume() [2/2]

const ModelVolume * Slic3r::GUI::GLGizmoTextureDisplacement::texture_volume ( ) const
private
3799{
3800 return const_cast<GLGizmoTextureDisplacement *>(this)->texture_volume();
3801}

References GLGizmoTextureDisplacement(), and texture_volume().

Here is the call graph for this function:

◆ texture_volume_raycaster_index()

int Slic3r::GUI::GLGizmoTextureDisplacement::texture_volume_raycaster_index ( ) const
private
925{
926 const ModelVolume *mv = texture_volume();
927 const ModelObject *mo = m_c->selection_info()->model_object();
928 if (mv == nullptr || mo == nullptr)
929 return -1;
930
931 int idx = -1, count = 0;
932 for (const ModelVolume *v : mo->volumes) {
933 if (!v->is_model_part())
934 continue;
935 if (v == mv) { idx = count; break; }
936 ++count;
937 }
938 return (idx >= 0 && idx < int(m_c->raycaster()->raycasters().size())) ? idx : -1;
939}

References Slic3r::GUI::GLGizmoBase::count, Slic3r::ModelVolume::is_model_part(), Slic3r::GUI::GLGizmoBase::m_c, texture_volume(), and Slic3r::ModelObject::volumes.

Referenced by render_painter_gizmo(), seam_edge_at(), and seam_vertex_at().

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

◆ toggle_seam_at()

void Slic3r::GUI::GLGizmoTextureDisplacement::toggle_seam_at ( const Vec2d & mouse_pos)
private
986{
987 TextureDisplacementLayer *layer = active_layer();
988 if (layer == nullptr)
989 return;
990 const std::pair<int, int> edge = seam_edge_at(mouse_pos);
991 if (edge.first < 0)
992 return;
993
994 Plater::TakeSnapshot snapshot(wxGetApp().plater(), _u8L("Mark texture seam"), UndoRedo::SnapshotType::GizmoAction);
995 auto &seams = layer->lscm_seam_edges;
996 if (const auto it = std::find(seams.begin(), seams.end(), edge); it != seams.end())
997 seams.erase(it);
998 else
999 seams.push_back(edge);
1001}

References _u8L, active_layer(), Slic3r::UndoRedo::GizmoAction, Slic3r::TextureDisplacementLayer::lscm_seam_edges, Slic3r::GUI::plater(), rebuild_preview(), and seam_edge_at().

Referenced by on_mouse_seam().

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

◆ unregister_grabbers_for_picking()

void Slic3r::GUI::GLGizmoBase::unregister_grabbers_for_picking ( )
protectedinherited
406{
407 for (size_t i = 0; i < m_grabbers.size(); ++i) {
408 m_grabbers[i].unregister_raycasters_for_picking();
409 }
410}

References m_grabbers.

Referenced by unregister_raycasters_for_picking().

Here is the caller graph for this function:

◆ unregister_raycasters_for_picking()

void Slic3r::GUI::GLGizmoBase::unregister_raycasters_for_picking ( )
inlineinherited
virtual void on_unregister_raycasters_for_picking()
Definition GLGizmoBase.hpp:268
void unregister_grabbers_for_picking()
Definition GLGizmoBase.cpp:405

References on_unregister_raycasters_for_picking(), and unregister_grabbers_for_picking().

Referenced by Slic3r::GUI::GLGizmosManager::activate_gizmo(), and Slic3r::GUI::GLCanvas3D::reload_scene().

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

◆ update_adjust_anchor()

bool Slic3r::GUI::GLGizmoTextureDisplacement::update_adjust_anchor ( )
private
3493{
3494 const ModelVolume *mv = texture_volume();
3495 m_adjust_anchor_valid = mv != nullptr &&
3496 compute_layer_paint_anchor(mv->mesh().its, mv->texture_displacement_facet(m_active_layer_slot).get_data(),
3498 return m_adjust_anchor_valid;
3499}
bool compute_layer_paint_anchor(const indexed_triangle_set &base_mesh, const TriangleSelector::TriangleSplittingData &facet_data, Vec3f &anchor_pos, Vec3f &anchor_normal)
Definition TextureDisplacement.cpp:2469

References Slic3r::compute_layer_paint_anchor(), Slic3r::FacetsAnnotation::get_data(), Slic3r::TriangleMesh::its, m_active_layer_slot, m_adjust_anchor_normal, m_adjust_anchor_pos, m_adjust_anchor_valid, Slic3r::ModelVolume::mesh(), Slic3r::ModelVolume::texture_displacement_facet(), and texture_volume().

Referenced by move_texture_layer(), and set_active_layer().

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

◆ update_contours()

void Slic3r::GUI::GLGizmoPainterBase::update_contours ( int i,
const TriangleMesh & vol_mesh,
float cursor_z,
float max_z,
float min_z ) const
privateinherited
491{
492 const Selection& selection = m_parent.get_selection();
493 const GLVolume* first_glvolume = selection.get_first_volume();
494 const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box();
495
496 const ModelObject* model_object = wxGetApp().model().objects[selection.get_object_idx()];
497 const int instance_idx = selection.get_instance_idx();
498
499 if (min_z < cursor_z && cursor_z < max_z) {
500 if (m_cut_contours[i].cut_z != cursor_z || m_cut_contours[i].object_id != model_object->id() || m_cut_contours[i].instance_idx != instance_idx) {
501 m_cut_contours[i].cut_z = cursor_z;
502
503 m_cut_contours[i].mesh = vol_mesh;
504
505 m_cut_contours[i].position = box.center();
506 m_cut_contours[i].shift = Vec3d::Zero();
507 m_cut_contours[i].object_id = model_object->id();
508 m_cut_contours[i].instance_idx = instance_idx;
509 m_cut_contours[i].contours.reset();
510
511 MeshSlicingParams slicing_params;
512 slicing_params.trafo = Transform3d::Identity().matrix();
513 const Polygons polys = slice_mesh(m_cut_contours[i].mesh.its, cursor_z, slicing_params);
514 if (!polys.empty()) {
515 m_cut_contours[i].contours.init_from(polys, static_cast<float>(cursor_z));
516 m_cut_contours[i].contours.set_color({ 1.0f, 1.0f, 1.0f, 1.0f });
517 }
518 }
519 else if (box.center() != m_cut_contours[i].position) {
520 m_cut_contours[i].shift = box.center() - m_cut_contours[i].position;
521 }
522 }
523 else
524 m_cut_contours[i].contours.reset();
525}
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:22
std::vector< Polygons > slice_mesh(const indexed_triangle_set &mesh, const std::vector< float > &zs, const MeshSlicingParams &params, std::function< void()> throw_on_cancel)
Definition TriangleMeshSlicer.cpp:1997

References Slic3r::BoundingBox3Base< PointType >::center(), Slic3r::GUI::Selection::get_first_volume(), Slic3r::GUI::Selection::get_instance_idx(), Slic3r::GUI::Selection::get_object_idx(), Slic3r::ObjectBase::id(), m_cut_contours, Slic3r::GUI::GLGizmoBase::m_parent, Slic3r::slice_mesh(), Slic3r::MeshSlicingParams::trafo, and Slic3r::GLVolume::transformed_convex_hull_bounding_box().

Referenced by render_cursor_height_range().

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

◆ update_from_model_object()

void Slic3r::GUI::GLGizmoTextureDisplacement::update_from_model_object ( bool first_update)
overrideprivatevirtual

Implements Slic3r::GUI::GLGizmoPainterBase.

3914{
3915 wxBusyCursor wait;
3916
3917 // Opening the gizmo puts the panel back where it was left: the layer and the mode come off the
3918 // volume, because they are the user's place in this model's stack, and the dock state off the
3919 // application config, because it is about their window. Only on open - this also runs on an
3920 // undo/redo and on a layer switch, where overriding what the user is doing now would be wrong.
3921 if (first_update) {
3922 if (const ModelVolume *mv = texture_volume(); mv != nullptr) {
3923 const TextureDisplacementOptions &opts = mv->texture_displacement_options;
3924 m_panel_mode = std::clamp(opts.panel_mode, 0, 1);
3925 // Only if that slot still holds a layer: the stack can have been edited by anything that
3926 // does not go through the panel, and a selection pointing at nothing shows an empty panel.
3927 const auto &stack = mv->texture_displacement_layers;
3928 if (std::any_of(stack.begin(), stack.end(),
3929 [&opts](const TextureDisplacementLayer &l) { return l.slot == opts.active_slot; }))
3930 m_active_layer_slot = opts.active_slot;
3931 }
3932 m_undocked = wxGetApp().app_config->get_bool(UNDOCKED_CONFIG_KEY);
3933 }
3934
3935 const ModelObject *mo = m_c->selection_info()->model_object();
3936 m_triangle_selectors.clear();
3937 // The base keeps the last mesh a fill tool hovered, and render_painter_gizmo() reads it as a hover that is
3938 // still on: a new set of selectors has none.
3940
3941 std::vector<ColorRGBA> ebt_colors;
3942 ebt_colors.push_back(GLVolume::NEUTRAL_COLOR);
3943 ebt_colors.push_back(TriangleSelectorGUI::enforcers_color);
3944 ebt_colors.push_back(TriangleSelectorGUI::blockers_color);
3945 m_selectors_stale = false;
3946 for (const ModelVolume *mv : mo->volumes) {
3947 if (!mv->is_model_part())
3948 continue;
3949
3950 const TriangleMesh *mesh = &mv->mesh();
3951 const TriangleSelector::TriangleSplittingData &data =
3952 mv->texture_displacement_facet(m_active_layer_slot).get_data();
3953 // The same bound TriangleSelector::deserialize() checks before it gives up - silently, with a
3954 // void return and no way to report it. A mask recorded before the mesh was replaced indexes
3955 // triangles that no longer exist, and the selector then comes back empty even though the mask
3956 // is not. That has to be caught here, because the next update_model_object() would otherwise
3957 // write the empty selector back over the mask: the paint would vanish, and the bake would
3958 // report "nothing is painted" about the very data the flush had just deleted.
3959 const size_t facet_count = mesh->its.indices.size();
3960 for (const TriangleSelector::TriangleBitStreamMapping &m : data.triangles_to_split)
3961 if (m.triangle_idx < 0 || size_t(m.triangle_idx) >= facet_count) {
3962 m_selectors_stale = true;
3963 break;
3964 }
3965 m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorPatch>(*mesh, ebt_colors));
3966 // The mask uses a single state (ENFORCER): a higher state in the file is reset to NONE.
3967 m_triangle_selectors.back()->deserialize(data, false, EnforcerBlockerType::ENFORCER);
3968 m_triangle_selectors.back()->request_update_render_data();
3969 }
3970
3971 // Start a freshly opened, never-textured volume with one layer already in place, so the panel is
3972 // ready to paint straight away rather than showing an empty layer list. Only on first open, and
3973 // only when there are none - never during an undo/redo or layer-switch reload (which also come
3974 // through here), where silently adding a layer would be wrong.
3975 if (first_update)
3976 if (ModelVolume *tv = texture_volume(); tv != nullptr && tv->texture_displacement_layers.empty()) {
3977 add_texture_layer(); // takes its own snapshot and rebuilds the preview
3978 return;
3979 }
3980
3982}
bool m_selectors_stale
Definition GLGizmoTextureDisplacement.hpp:759
static ColorRGBA blockers_color
Definition GLGizmoPainterBase.hpp:67
static ColorRGBA enforcers_color
Definition GLGizmoPainterBase.hpp:66
std::vector< TextureDisplacementLayer > texture_displacement_layers
Definition Model.hpp:974

References Slic3r::TextureDisplacementOptions::active_slot, add_texture_layer(), Slic3r::GUI::TriangleSelectorGUI::blockers_color, Slic3r::ENFORCER, Slic3r::GUI::TriangleSelectorGUI::enforcers_color, Slic3r::FacetsAnnotation::get_data(), Slic3r::ModelVolume::is_model_part(), Slic3r::TriangleMesh::its, m_active_layer_slot, Slic3r::GUI::GLGizmoBase::m_c, m_panel_mode, Slic3r::GUI::GLGizmoPainterBase::m_seed_fill_last_mesh_id, m_selectors_stale, Slic3r::GUI::GLGizmoPainterBase::m_triangle_selectors, m_undocked, Slic3r::ModelVolume::mesh(), Slic3r::GLVolume::NEUTRAL_COLOR, Slic3r::TextureDisplacementOptions::panel_mode, rebuild_preview(), Slic3r::ModelVolume::texture_displacement_facet(), Slic3r::ModelVolume::texture_displacement_layers, texture_volume(), Slic3r::TriangleSelector::TriangleBitStreamMapping::triangle_idx, Slic3r::TriangleSelector::TriangleSplittingData::triangles_to_split, UNDOCKED_CONFIG_KEY, and Slic3r::ModelObject::volumes.

Referenced by bake(), move_texture_layer(), queue_prepare(), remove_texture_layer(), set_active_layer(), smooth_model(), and subdivide_model().

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

◆ update_image_adjust_preview()

void Slic3r::GUI::GLGizmoTextureDisplacement::update_image_adjust_preview ( )
private
4926{
4927 if (m_adjust_preview_source.empty())
4928 return;
4929
4930 // Collect a finished pass. wait_for(0) rather than get() outright: this runs inside the frame, and
4931 // blocking on the worker here would hand back to the UI thread the very stall the worker exists to
4932 // spare it.
4933 if (m_adjust_preview_job.valid() &&
4934 m_adjust_preview_job.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
4935 const DecodedHeightTexture adjusted = m_adjust_preview_job.get();
4937 if (std::unique_ptr<GLTexture> tex = upload_height_thumbnail(adjusted, ADJUST_PREVIEW_MAX_PX)) {
4938 m_adjust_preview_tex = std::move(tex);
4941 }
4942 }
4943
4944 // One pass at a time: whatever the sliders did while it ran is picked up by the next launch, so a
4945 // drag collapses to the value the sliders ended on instead of queuing one pass per frame.
4948 // Captured by value: the pass must not read members the UI thread goes on editing while it runs.
4949 m_adjust_preview_job = std::async(std::launch::async,
4951 return apply_texture_adjustments(src, adj);
4952 });
4953 }
4954
4955 // A pass in flight has no other way to get its result on screen: without a redraw request the canvas
4956 // can sit idle, and the preview would only catch up on the next unrelated frame.
4957 if (m_adjust_preview_job.valid())
4958 m_parent.set_as_dirty();
4959}
TextureAdjustments m_adjust_preview_running
Definition GLGizmoTextureDisplacement.hpp:586

References Slic3r::apply_texture_adjustments(), m_adjust, m_adjust_preview_job, m_adjust_preview_running, m_adjust_preview_shown, m_adjust_preview_source, m_adjust_preview_tex, m_adjust_preview_valid, and Slic3r::GUI::GLGizmoBase::m_parent.

Referenced by render_image_adjust_window().

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

◆ update_items_state()

bool Slic3r::GUI::GLGizmoBase::update_items_state ( )
inherited
361{
362 bool res = m_dirty;
363 m_dirty = false;
364 return res;
365}

References m_dirty.

◆ update_model_object()

void Slic3r::GUI::GLGizmoTextureDisplacement::update_model_object ( )
overrideprivatevirtual

Implements Slic3r::GUI::GLGizmoPainterBase.

3880{
3882 bool updated = false;
3883 ModelObject *mo = m_c->selection_info()->model_object();
3884 int idx = -1;
3885 for (ModelVolume *mv : mo->volumes) {
3886 if (!mv->is_model_part())
3887 continue;
3888 ++idx;
3889 FacetsAnnotation &facet = mv->texture_displacement_facet(m_active_layer_slot);
3890 // See m_selectors_stale: the mask could not be loaded into this selector, so an empty selector
3891 // here is "failed to load", not "nothing painted", and writing it back would erase the paint.
3892 // A selector that does hold something is the user's own work and must be flushed as usual.
3893 if (m_selectors_stale && !facet.empty() && m_triangle_selectors[idx]->serialize().triangles_to_split.empty())
3894 continue;
3895 updated |= facet.set(*m_triangle_selectors[idx]);
3896 }
3897
3898 // The fast (shaded) preview reads the live selector, so it has to be rebuilt after any stroke that
3899 // flushes here - not only when set() reports a change. Rebuilding it via rebuild_preview() below
3900 // is gated on `updated`, which misses e.g. the first paint into a slot; marking it dirty makes the
3901 // render loop (render_painter_gizmo) rebuild it next frame regardless. Without this, fast preview -
3902 // now the default view - stayed blank until a full reload (select-whole-model / reopen).
3904
3905 if (updated) {
3906 const ModelObjectPtrs &mos = wxGetApp().model().objects;
3907 wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin());
3908 m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
3910 }
3911}

References Slic3r::FacetsAnnotation::empty(), Slic3r::ModelVolume::is_model_part(), m_active_layer_slot, Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, m_selectors_stale, m_shaded_preview_dirty, Slic3r::GUI::GLGizmoPainterBase::m_triangle_selectors, rebuild_preview(), Slic3r::FacetsAnnotation::set(), store_panel_state(), Slic3r::ModelVolume::texture_displacement_facet(), and Slic3r::ModelObject::volumes.

Referenced by bake(), bake_standard(), move_texture_layer(), on_render_input_window(), remesh_model(), run_stage_debug(), select_visible_faces(), select_whole_model(), set_active_layer(), smooth_model(), and subdivide_model_adaptive().

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

◆ update_projector()

void Slic3r::GUI::GLGizmoTextureDisplacement::update_projector ( )
private
3199{
3200 if (m_projector_frame == nullptr || !m_projector_frame->IsShown())
3201 return;
3202
3203 const TextureDisplacementLayer *layer = active_layer();
3204 if (layer == nullptr || layer->projection_method != TextureProjectionMethod::ViewProjected) {
3205 m_projector_frame->set_texture({}, 0, 0);
3206 m_projector_tex_source = nullptr;
3208 return;
3209 }
3210
3211 // Only re-upload when the pixels actually changed - this is reached from the panel's per-edit
3212 // flush, and rebuilding the bitmap from identical bytes every time would be pure waste.
3213 if (m_projector_tex_source != layer->image_data.get() || m_projector_tex_smoothing != layer->smoothing) {
3214 const DecodedHeightTexture height = decode_height_texture(*layer);
3215 if (height.empty())
3216 m_projector_frame->set_texture({}, 0, 0);
3217 else
3218 m_projector_frame->set_texture(height.pixels, height.width, height.height);
3219 m_projector_tex_source = layer->image_data.get();
3220 m_projector_tex_smoothing = layer->smoothing;
3221 }
3222}

References active_layer(), Slic3r::decode_height_texture(), Slic3r::TextureDisplacementLayer::image_data, m_projector_frame, m_projector_tex_smoothing, m_projector_tex_source, Slic3r::TextureDisplacementLayer::projection_method, Slic3r::TextureDisplacementLayer::smoothing, and Slic3r::ViewProjected.

Referenced by show_projector().

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

◆ update_raycast_cache()

void Slic3r::GUI::GLGizmoPainterBase::update_raycast_cache ( const Vec2d & mouse_position,
const Camera & camera,
const std::vector< Transform3d > & trafo_matrices ) const
privateinherited
1112{
1113 if (m_rr.mouse_position == mouse_position) {
1114 // Same query as last time - the answer is already in the cache.
1115 return;
1116 }
1117
1118 Vec3f normal = Vec3f::Zero();
1119 Vec3f hit = Vec3f::Zero();
1120 size_t facet = 0;
1121 Vec3f closest_hit = Vec3f::Zero();
1122 double closest_hit_squared_distance = std::numeric_limits<double>::max();
1123 size_t closest_facet = 0;
1124 int closest_hit_mesh_id = -1;
1125
1126 // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh
1127 for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) {
1128
1129 if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(
1130 mouse_position,
1131 trafo_matrices[mesh_id],
1132 camera,
1133 hit,
1134 normal,
1135 m_c->object_clipper()->get_clipping_plane(),
1136 &facet,
1137 m_parent.get_canvas_type() != GLCanvas3D::CanvasAssembleView))
1138 {
1139 // In case this hit is clipped, skip it.
1140 if (is_mesh_point_clipped(hit.cast<double>(), trafo_matrices[mesh_id]))
1141 continue;
1142
1143 // Is this hit the closest to the camera so far?
1144 double hit_squared_distance = (camera.get_position()-trafo_matrices[mesh_id]*hit.cast<double>()).squaredNorm();
1145 if (hit_squared_distance < closest_hit_squared_distance) {
1146 closest_hit_squared_distance = hit_squared_distance;
1147 closest_facet = facet;
1148 closest_hit_mesh_id = mesh_id;
1149 closest_hit = hit;
1150 }
1151 }
1152 }
1153
1154 m_rr = {mouse_position, closest_hit_mesh_id, closest_hit, closest_facet};
1155}
bool is_mesh_point_clipped(const Vec3d &point, const Transform3d &trafo) const
Definition GLGizmoPainterBase.cpp:527

References Slic3r::GUI::GLCanvas3D::CanvasAssembleView, Slic3r::GUI::Camera::get_position(), is_mesh_point_clipped(), Slic3r::GUI::GLGizmoBase::m_c, Slic3r::GUI::GLGizmoBase::m_parent, m_rr, and Slic3r::normal().

Referenced by get_projected_height_range(), get_projected_mouse_positions(), gizmo_event(), render_cursor(), and render_follows_cursor().

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

◆ update_render_colors()

void Slic3r::GUI::GLGizmoBase::update_render_colors ( )
staticinherited

References AXES_COLOR, Slic3r::RenderColor::colors, FLATTEN_COLOR, FLATTEN_HOVER_COLOR, Slic3r::GUI::ImGuiWrapper::from_ImVec4(), RenderCol_Flatten_Plane, RenderCol_Flatten_Plane_Hover, RenderCol_Grabber_X, RenderCol_Grabber_Y, and RenderCol_Grabber_Z.

Referenced by Slic3r::GUI::GLCanvas3D::_render_style_editor().

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

◆ update_uv_editor()

void Slic3r::GUI::GLGizmoTextureDisplacement::update_uv_editor ( )
private
2582{
2583 Plater *plater = wxGetApp().plater();
2584 UVEditorCanvas *uv_canvas = plater->get_uv_editor_canvas();
2585 if (uv_canvas == nullptr)
2586 return;
2587 // Wired on every call rather than with the first unwrap: Unwrap itself is one of the pane's commands.
2588 uv_canvas->set_command_callback([this](UVEditorCanvas::Command cmd, float value) { on_uv_command(int(cmd), value); });
2589
2590 const ModelVolume *mv = texture_volume();
2591 TextureDisplacementLayer *layer = active_layer();
2592 // The pane is opened only on the user's explicit request (m_show_uv_editor), and only for an LSCM
2593 // layer - never automatically just because something is painted. Keep the cached state/unwrap so
2594 // that switching the toggle back on re-shows instantly (and re-solves if the paint changed while
2595 // it was hidden, via the state comparison below).
2596 if (!m_show_uv_editor || mv == nullptr || layer == nullptr ||
2597 layer->projection_method != TextureProjectionMethod::LSCM) {
2598 m_uv_editor_bg = UVBackground::None; // the next time it is shown, upload the background afresh
2600 plater->show_uv_editor(false);
2601 return;
2602 }
2603
2604 // A vertex/edge edit committing (or an undo reverting one) changes the per-vertex UV overrides
2605 // without going through the Unwrap button. Detect that and force a re-solve, so the pane's geometry
2606 // stays in step with what will bake - the one exception to "only re-solve on Unwrap".
2607 {
2608 size_t sig = 1469598103934665603ull; // FNV-1a seed
2609 const auto mix = [&sig](uint64_t x) { sig = (sig ^ x) * 1099511628211ull; };
2610 mix(layer->lscm_uv_overrides.size());
2611 for (const auto &[v, uv] : layer->lscm_uv_overrides) {
2612 mix(uint64_t(uint32_t(v)));
2613 mix(uint64_t(uint32_t(int32_t(std::llround(uv.x() * 1024.f)))));
2614 mix(uint64_t(uint32_t(int32_t(std::llround(uv.y() * 1024.f)))));
2615 }
2616 if (sig != m_uv_overrides_sig) {
2617 m_uv_overrides_sig = sig;
2618 m_uv_unwrap_pending = true;
2619 }
2620 }
2621
2622 UVEditorState state;
2623 state.slot = m_active_layer_slot;
2624 state.image_data = layer->image_data.get();
2625 state.seam_angle = layer->lscm_seam_angle_deg;
2626 state.padding = layer->island_padding_mm;
2627 state.facets = mv->texture_displacement_facet(m_active_layer_slot).get_data();
2628 state.seam_edges = layer->lscm_seam_edges;
2629
2630 // The re-solve happens only when the user pressed "Unwrap" (m_uv_unwrap_pending). Every other call
2631 // into here - a paint stroke ending, a slider release, the check mode changing - must not pay for
2632 // a fresh LSCM solve; it just re-applies the cheap affine transforms over whatever unwrap already
2633 // exists. If the paint changed underneath but the user hasn't asked to re-unwrap, the pane keeps
2634 // showing the last unwrap on purpose (that is the whole point of making it an explicit action).
2635 bool unwrap_changed = false;
2636 if (m_uv_unwrap_pending) {
2637 m_uv_unwrap_pending = false;
2638 // World millimetres, the space the bake unwraps in - otherwise the pane would lay the islands
2639 // out at the volume's own scale and show the texture at a different size than it bakes at.
2640 const indexed_triangle_set patch = patch_in_world(extract_painted_patch(mv->mesh().its, state.facets));
2641 if (patch.indices.empty()) {
2642 // Nothing painted to unwrap: clear what the pane showed but keep it open - its status line says what to do.
2644 m_uv_editor_unwrap = PatchUnwrap{};
2646 uv_canvas->set_islands({});
2648 plater->show_uv_editor(true);
2649 return;
2650 }
2651 // Padding disabled (0): the user asked to pack islands with no gap between them.
2652 m_uv_editor_unwrap = compute_patch_unwrap(patch, layer->lscm_seam_angle_deg, 0.f, layer->lscm_seam_edges);
2653 m_island_overlay_glmodel.reset(); // the islands were renumbered: rebuilt from the pane's selection next frame
2655 // Re-apply any stored UV edits onto the fresh unwrap, so the pane shows exactly what
2656 // compute_lscm_uvs() will bake (which applies the same overrides).
2657 apply_lscm_uv_overrides(m_uv_editor_unwrap, layer->lscm_uv_overrides);
2658 m_uv_editor_state = std::move(state);
2659 unwrap_changed = true;
2660 // Precompute the distortion heatmap now, while the patch is in hand - relative stretch doesn't
2661 // change when islands are only moved, so this need not be redone on a drag. It is fed to the
2662 // canvas below only while the Distortion check mode is on.
2664 }
2665
2666 // The pane background either mirrors the height texture (default) or shows a UV checker (#7). It is uploaded
2667 // before, and independently of, the unwrap - the texture belongs on screen before anything is unwrapped - and
2668 // only when the choice, the layer's image or its smoothing changed, because the height image is large.
2670 const bool bg_smoothing_changed = (desired_bg == UVBackground::Height) &&
2671 (m_uv_editor_bg_smoothing != layer->smoothing ||
2672 !(m_uv_editor_bg_adjust == layer->preview_adjust));
2673 if (desired_bg != m_uv_editor_bg || layer->image_data.get() != m_uv_editor_bg_image || bg_smoothing_changed) {
2674 m_uv_editor_bg_smoothing = layer->smoothing;
2675 m_uv_editor_bg_adjust = layer->preview_adjust;
2676 m_uv_editor_bg_image = layer->image_data.get();
2677 m_uv_editor_bg = desired_bg;
2678 if (desired_bg == UVBackground::Checker) {
2679 // An even squares-per-axis count so the pattern tiles seamlessly across the UV unit
2680 // boundary (texcoord == position repeats it once per tile). Softened grays, not pure
2681 // black/white, so it doesn't fight the island wires drawn over it.
2682 constexpr int tex = 512, squares = 8, cell = tex / squares;
2683 std::vector<unsigned char> checker(size_t(tex) * size_t(tex));
2684 for (int y = 0; y < tex; ++y)
2685 for (int x = 0; x < tex; ++x)
2686 checker[size_t(y) * tex + x] = ((x / cell + y / cell) & 1) ? 205 : 70;
2687 uv_canvas->set_background_texture(checker, tex, tex);
2688 } else {
2689 const DecodedHeightTexture height = decode_height_texture(*layer);
2690 if (!height.empty()) {
2691 uv_canvas->set_background_texture(height.pixels, height.width, height.height);
2692 } else {
2693 // Not recorded as uploaded, so the next update tries again rather than keeping a blank backdrop.
2694 uv_canvas->set_background_texture({}, 0, 0);
2696 }
2697 }
2698 }
2699 // The backdrop's extent and repeat follow the tile settings, with or without an unwrap.
2700 uv_canvas->set_uv_transform(layer->tiling_scale, layer->rotation_deg, layer->tile_enabled,
2701 layer->tile_method == TextureTileMethod::MirroredRepeat);
2702
2703 if (m_uv_editor_unwrap.empty()) {
2704 // Nothing unwrapped yet: the pane opens anyway, because Unwrap itself lives in it. Its canvas shows the
2705 // texture and its status line says to paint the area and press Unwrap.
2707 plater->show_uv_editor(true);
2708 return;
2709 }
2710
2711 // Grow (never shrink) the layer's island list to cover every chart. Shrinking would throw away a
2712 // hand placement the moment a stroke temporarily merged two islands, and a stale extra entry is
2713 // harmless - island_transform_matrix() only looks up the charts that actually exist.
2714 if (layer->islands.size() < size_t(m_uv_editor_unwrap.chart_count))
2715 layer->islands.resize(size_t(m_uv_editor_unwrap.chart_count));
2716
2717 // Connected-net layout (on by default): a *fresh* unwrap is unfolded so adjacent charts sit
2718 // edge-to-edge (cube -> a net), rather than as separately packed squares. Only when the user pressed
2719 // Unwrap (m_uv_apply_connected_net) - a re-segmentation renumbers charts anyway, so any hand
2720 // placement from before is already meaningless. A *refresh* re-solve (a committed vertex edit, or an
2721 // undo) must NOT relayout, or it would throw away every island placement on every vertex edit.
2722 if (unwrap_changed && m_uv_apply_connected_net && layer->auto_connect_islands) {
2723 std::vector<TextureIsland> net = compute_connected_net(m_uv_editor_unwrap);
2724 if (net.size() == size_t(m_uv_editor_unwrap.chart_count)) {
2725 if (layer->islands.size() < net.size())
2726 layer->islands.resize(net.size());
2727 for (size_t i = 0; i < net.size(); ++i)
2728 layer->islands[i] = net[i];
2729 }
2730 }
2731 if (unwrap_changed)
2732 m_uv_apply_connected_net = false; // consumed; a refresh re-solve leaves placements alone
2733
2734 // The geometry goes over in the unwrap's *raw* mm coordinates and is only re-uploaded when the
2735 // unwrap itself changed. Everything a slider or a drag can touch - island placement, tiling,
2736 // rotation, offset - is an affine map on top of that, so it goes over as one 2x3 matrix per
2737 // island instead. That is the whole reason dragging an island is now free: a patch of a million
2738 // triangles has a million UVs to re-transform and re-upload otherwise, and it was doing exactly
2739 // that on every single mouse-move event.
2740 if (unwrap_changed) {
2741 UVEditorCanvas::Islands view;
2742 view.uvs = m_uv_editor_unwrap.uvs;
2743 view.indices = m_uv_editor_unwrap.indices;
2744 view.vertex_island = m_uv_editor_unwrap.vertex_chart;
2745 view.boundary_edges = m_uv_editor_unwrap.boundary_edges;
2746 view.island_count = m_uv_editor_unwrap.chart_count;
2747
2748 uv_canvas->set_island_edit_callback(
2749 [this](int island, const Vec2f &offset_delta, float rotation_delta, float scale_factor, bool finished) {
2750 on_island_edited(island, offset_delta, rotation_delta, scale_factor, finished);
2751 });
2752 uv_canvas->set_vertex_edit_callback(
2753 [this](const std::vector<std::pair<int, Vec2f>> &edits) { on_uv_vertex_edited(edits); });
2754 uv_canvas->set_islands(std::move(view));
2755 }
2756
2757 uv_canvas->set_select_mode(static_cast<UVEditorCanvas::SelectMode>(m_uv_select_mode));
2758 uv_canvas->set_island_transforms(uv_editor_island_transforms(*layer));
2759 // The distortion heatmap tints the island fills only while its check mode is on; otherwise the
2760 // canvas falls back to its default light-green wash.
2762 uv_canvas->set_island_fill_colors(m_uv_editor_distortion_colors);
2763 else
2764 uv_canvas->set_island_fill_colors({});
2765
2767 plater->show_uv_editor(true);
2768}
void on_island_edited(int island, const Vec2f &offset_delta, float rotation_delta, float scale_factor, bool finished)
Definition GLGizmoTextureDisplacement.cpp:3383
float m_uv_editor_bg_smoothing
Definition GLGizmoTextureDisplacement.hpp:1085
size_t m_uv_overrides_sig
Definition GLGizmoTextureDisplacement.hpp:1080
const void * m_uv_editor_bg_image
Definition GLGizmoTextureDisplacement.hpp:1087
void on_uv_command(int cmd, float value)
Definition GLGizmoTextureDisplacement.cpp:2831
void on_uv_vertex_edited(const std::vector< std::pair< int, Vec2f > > &edits)
Definition GLGizmoTextureDisplacement.cpp:3463
TextureAdjustments m_uv_editor_bg_adjust
Definition GLGizmoTextureDisplacement.hpp:1086
void compute_uv_editor_distortion_colors(const indexed_triangle_set &patch)
Definition GLGizmoTextureDisplacement.cpp:2770
UVBackground
Definition GLGizmoTextureDisplacement.hpp:1083
@ Checker
Definition GLGizmoTextureDisplacement.hpp:1083
@ Height
Definition GLGizmoTextureDisplacement.hpp:1083
class UVEditorCanvas * get_uv_editor_canvas()
Definition Plater.cpp:22099
void show_uv_editor(bool show)
Definition Plater.cpp:22104
SelectMode
Definition UVEditorCanvas.hpp:171
void set_command_callback(CommandFn fn)
Definition UVEditorCanvas.hpp:120
wxColour mix(const wxColour &a, const wxColour &b, double t)
Definition UVEditorCanvas.cpp:1786
PatchUnwrap compute_patch_unwrap(const indexed_triangle_set &patch, float seam_angle_deg, float padding_mm, const std::vector< std::pair< int, int > > &seam_edges)
Definition TextureDisplacement.cpp:1230
std::vector< bool > apply_lscm_uv_overrides(PatchUnwrap &unwrap, const std::vector< std::pair< int, Vec2f > > &overrides)
Definition TextureDisplacement.cpp:1969

References active_layer(), Slic3r::apply_lscm_uv_overrides(), Slic3r::TextureDisplacementLayer::auto_connect_islands, Slic3r::GUI::UVEditorCanvas::Islands::boundary_edges, Checker, Slic3r::compute_connected_net(), Slic3r::compute_patch_unwrap(), compute_uv_editor_distortion_colors(), Slic3r::decode_height_texture(), Distortion, Slic3r::extract_painted_patch(), Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::facets, Slic3r::FacetsAnnotation::get_data(), Height, Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::image_data, Slic3r::TextureDisplacementLayer::image_data, Slic3r::GUI::UVEditorCanvas::Islands::indices, Slic3r::GUI::UVEditorCanvas::Islands::island_count, Slic3r::TextureDisplacementLayer::island_padding_mm, Slic3r::TextureDisplacementLayer::islands, Slic3r::TriangleMesh::its, Slic3r::LSCM, Slic3r::TextureDisplacementLayer::lscm_seam_angle_deg, Slic3r::TextureDisplacementLayer::lscm_seam_edges, Slic3r::TextureDisplacementLayer::lscm_uv_overrides, m_active_layer_slot, m_island_overlay_glmodel, m_island_overlay_selection, m_show_uv_editor, m_uv_apply_connected_net, m_uv_check_mode, m_uv_editor_bg, m_uv_editor_bg_adjust, m_uv_editor_bg_image, m_uv_editor_bg_smoothing, m_uv_editor_distortion_colors, m_uv_editor_state, m_uv_editor_unwrap, m_uv_overrides_sig, m_uv_select_mode, m_uv_unwrap_pending, Slic3r::ModelVolume::mesh(), Slic3r::MirroredRepeat, None, on_island_edited(), on_uv_command(), on_uv_vertex_edited(), Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::padding, patch_in_world(), Slic3r::GUI::plater(), Slic3r::TextureDisplacementLayer::preview_adjust, Slic3r::TextureDisplacementLayer::projection_method, push_uv_pane_state(), Slic3r::TextureDisplacementLayer::rotation_deg, Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::seam_angle, Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::seam_edges, Slic3r::GUI::UVEditorCanvas::set_background_texture(), Slic3r::GUI::UVEditorCanvas::set_command_callback(), Slic3r::GUI::UVEditorCanvas::set_island_edit_callback(), Slic3r::GUI::UVEditorCanvas::set_island_fill_colors(), Slic3r::GUI::UVEditorCanvas::set_island_transforms(), Slic3r::GUI::UVEditorCanvas::set_islands(), Slic3r::GUI::UVEditorCanvas::set_select_mode(), Slic3r::GUI::UVEditorCanvas::set_uv_transform(), Slic3r::GUI::UVEditorCanvas::set_vertex_edit_callback(), Slic3r::GUI::GLGizmoTextureDisplacement::UVEditorState::slot, Slic3r::TextureDisplacementLayer::smoothing, Slic3r::ModelVolume::texture_displacement_facet(), texture_volume(), Slic3r::TextureDisplacementLayer::tile_enabled, Slic3r::TextureDisplacementLayer::tile_method, Slic3r::TextureDisplacementLayer::tiling_scale, uv_editor_island_transforms(), Slic3r::GUI::UVEditorCanvas::Islands::uvs, and Slic3r::GUI::UVEditorCanvas::Islands::vertex_island.

Referenced by apply_view_mode(), rebuild_preview(), and run_uv_command().

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

◆ use_grabbers()

bool Slic3r::GUI::GLGizmoBase::use_grabbers ( const wxMouseEvent & mouse_event)
protectedinherited

function which Set up m_dragging and call functions on_start_dragging / on_dragging / on_stop_dragging

Parameters
mouse_eventKeep information about mouse click
Returns
same as on_mouse
444 {
445 bool is_dragging_finished = false;
446 if (mouse_event.Moving()) {
447 // it should not happen but for sure
448 assert(!m_dragging);
449 if (m_dragging) is_dragging_finished = true;
450 else return false;
451 }
452
453 if (mouse_event.LeftDown()) {
454 Selection &selection = m_parent.get_selection();
455 if (!selection.is_empty() && m_hover_id != -1 /* &&
456 (m_grabbers.empty() || m_hover_id < static_cast<int>(m_grabbers.size()))*/) {
457 selection.setup_cache();
458
459 m_dragging = true;
460 for (auto &grabber : m_grabbers) grabber.dragging = false;
461// if (!m_grabbers.empty() && m_hover_id < int(m_grabbers.size()))
462// m_grabbers[m_hover_id].dragging = true;
463
465
466 // Let the plater know that the dragging started
467 m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED));
468 m_parent.set_as_dirty();
469 return true;
470 }
471 } else if (m_dragging) {
472 // when mouse cursor leave window than finish actual dragging operation
473 bool is_leaving = mouse_event.Leaving() && !m_parent.has_mouse_capture(); // ORCA keep tracking mouse position while drag active and cursor not in window bounds
474 if (mouse_event.Dragging()) {
475 Point mouse_coord(mouse_event.GetX(), mouse_event.GetY());
476 auto ray = m_parent.mouse_ray(mouse_coord);
477 UpdateData data(ray, mouse_coord);
478
479 on_dragging(data);
480
481 wxGetApp().obj_manipul()->set_dirty();
482 m_parent.set_as_dirty();
483 return true;
484 }
485 else if (mouse_event.LeftUp() || is_leaving || is_dragging_finished) {
486 do_stop_dragging(is_leaving);
487 return true;
488 }
489 }
490 return false;
491}
virtual void on_dragging(const UpdateData &data)
Definition GLGizmoBase.hpp:255
virtual void on_start_dragging()
Definition GLGizmoBase.hpp:253
void do_stop_dragging(bool perform_mouse_cleanup)
Definition GLGizmoBase.cpp:493
Definition GLGizmoBase.hpp:133

References do_stop_dragging(), Slic3r::GUI::Selection::is_empty(), m_dragging, m_grabbers, m_hover_id, m_parent, on_dragging(), on_start_dragging(), Slic3r::GUI::Selection::setup_cache(), and Slic3r::GUI::wxMouseEvent.

Referenced by Slic3r::GUI::GLGizmoBrimEars::on_mouse(), Slic3r::GUI::GLGizmoCut3D::on_mouse(), Slic3r::GUI::GLGizmoMove3D::on_mouse(), Slic3r::GUI::GLGizmoRotate3D::on_mouse(), Slic3r::GUI::GLGizmoRotate::on_mouse(), Slic3r::GUI::GLGizmoScale3D::on_mouse(), Slic3r::GUI::GLGizmoEmboss::on_mouse_for_rotation(), and Slic3r::GUI::GLGizmoSVG::on_mouse_for_rotation().

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

◆ uv_editor_island_transforms()

std::vector< Eigen::Matrix< float, 2, 3 > > Slic3r::GUI::GLGizmoTextureDisplacement::uv_editor_island_transforms ( const TextureDisplacementLayer & layer)
private
3350{
3351 const float uv_scale = (layer.tiling_scale > 1e-6f) ? (1.f / layer.tiling_scale) : 1.f;
3352 const float rad = layer.rotation_deg * float(M_PI) / 180.f;
3353 const float cs = std::cos(rad) * uv_scale;
3354 const float sn = std::sin(rad) * uv_scale;
3355
3356 Eigen::Matrix2f uv_linear;
3357 uv_linear << cs, -sn,
3358 sn, cs;
3359
3360 m_uv_editor_bbox_min = Vec2f(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
3361 m_uv_editor_bbox_max = Vec2f(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
3362
3363 std::vector<Eigen::Matrix<float, 2, 3>> transforms(size_t(std::max(m_uv_editor_unwrap.chart_count, 0)));
3364 for (int c = 0; c < m_uv_editor_unwrap.chart_count; ++c) {
3365 const Eigen::Matrix<float, 2, 3> island = island_transform_matrix(c, m_uv_editor_unwrap, layer.islands);
3366 Eigen::Matrix<float, 2, 3> &m = transforms[size_t(c)];
3367 m.block<2, 2>(0, 0) = uv_linear * island.block<2, 2>(0, 0);
3368 m.col(2) = uv_linear * island.col(2) + layer.offset;
3369 }
3370
3371 // Only for the panel's readout; the canvas computes its own bounds.
3372 for (size_t i = 0; i < m_uv_editor_unwrap.uvs.size(); ++i) {
3373 const int c = m_uv_editor_unwrap.vertex_chart[i];
3374 if (c < 0 || size_t(c) >= transforms.size())
3375 continue;
3376 const Vec2f uv = transforms[size_t(c)].block<2, 2>(0, 0) * m_uv_editor_unwrap.uvs[i] + transforms[size_t(c)].col(2);
3379 }
3380 return transforms;
3381}
Vec2f m_uv_editor_bbox_max
Definition GLGizmoTextureDisplacement.hpp:1177
Vec2f m_uv_editor_bbox_min
Definition GLGizmoTextureDisplacement.hpp:1176

References Slic3r::island_transform_matrix(), Slic3r::TextureDisplacementLayer::islands, M_PI, m_uv_editor_bbox_max, m_uv_editor_bbox_min, m_uv_editor_unwrap, Slic3r::TextureDisplacementLayer::offset, Slic3r::TextureDisplacementLayer::rotation_deg, and Slic3r::TextureDisplacementLayer::tiling_scale.

Referenced by on_island_edited(), rebuild_shaded_preview_mesh(), and update_uv_editor().

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

◆ v2_recommendation()

const V2Resolution & Slic3r::GUI::GLGizmoTextureDisplacement::v2_recommendation ( const ModelVolume & mv)
private
5676{
5677 // Rebuilt only when something it depends on changes: the surface area scan is O(triangles) and the
5678 // panel asks for this every frame.
5679 std::string key = std::to_string(mv.id().id) + ":" + std::to_string(mv.mesh().facets_count());
5680 for (const TextureDisplacementLayer &l : mv.texture_displacement_layers)
5681 key += Slic3r::format("|%1%:%2%:%3%:%4%", l.slot, reinterpret_cast<uintptr_t>(l.image_data.get()), l.tiling_scale, l.depth_mm);
5683 for (int i = 0; i < 12; ++i)
5684 key += Slic3r::format(",%1%", trafo.matrix()(i / 4, i % 4));
5685 if (key != m_v2_rec_key) {
5686 m_v2_rec = recommend_v2_resolution(mv.mesh().its, mv.texture_displacement_layers, trafo);
5687 m_v2_rec_key = std::move(key);
5688 }
5689 return m_v2_rec;
5690}
V2Resolution m_v2_rec
Definition GLGizmoTextureDisplacement.hpp:886
std::string m_v2_rec_key
Definition GLGizmoTextureDisplacement.hpp:887
V2Resolution recommend_v2_resolution(const indexed_triangle_set &mesh, const std::vector< TextureDisplacementLayer > &layers, const Transform3d &volume_to_world)
Definition TextureDisplacement.cpp:707

References Slic3r::TextureDisplacementLayer::depth_mm, Slic3r::TriangleMesh::facets_count(), Slic3r::format(), Slic3r::ObjectBase::id(), Slic3r::ObjectID::id, Slic3r::TextureDisplacementLayer::image_data, Slic3r::TriangleMesh::its, m_v2_rec, m_v2_rec_key, Slic3r::ModelVolume::mesh(), Slic3r::recommend_v2_resolution(), Slic3r::TextureDisplacementLayer::slot, Slic3r::ModelVolume::texture_displacement_layers, Slic3r::texture_displacement_volume_to_world(), and Slic3r::TextureDisplacementLayer::tiling_scale.

Here is the call graph for this function:

◆ view_mode()

int Slic3r::GUI::GLGizmoTextureDisplacement::view_mode ( ) const
private
2865{
2866 return m_use_shaded_preview ? 1 :
2869}

References Checker, Distortion, m_use_shaded_preview, and m_uv_check_mode.

Referenced by follow_island_highlight().

Here is the caller graph for this function:

◆ wants_enter_leave_snapshots()

bool Slic3r::GUI::GLGizmoPainterBase::wants_enter_leave_snapshots ( ) const
inlineoverrideprotectedvirtualinherited

Reimplemented from Slic3r::GUI::GLGizmoBase.

406{ return true; }

Member Data Documentation

◆ AXES_COLOR

◆ AXES_HOVER_COLOR

std::array< ColorRGBA, 3 > Slic3r::GUI::GLGizmoBase::AXES_HOVER_COLOR
staticinherited
Initial value:
= {{
{ColorRGBA::X().r() * 1.2f, ColorRGBA::X().g() * 1.4f, ColorRGBA::X().b() * 1.4f, 1.0f},
{ColorRGBA::Y().r() * 1.2f, ColorRGBA::Y().g() * 1.2f, ColorRGBA::Y().b() * 1.2f, 1.0f},
{ColorRGBA::Z().r() * 1.2f, ColorRGBA::Z().g() * 1.2f, ColorRGBA::Z().b() * 1.2f, 1.0f},
}}
float b() const
Definition Color.hpp:107
float r() const
Definition Color.hpp:105
float g() const
Definition Color.hpp:106

Referenced by Slic3r::GUI::GLGizmoMove3D::on_render(), Slic3r::GUI::GLGizmoRotate::on_render(), and Slic3r::GUI::GLGizmoScale3D::update_grabbers_data().

◆ BASE_ID

const unsigned int Slic3r::GUI::GLGizmoBase::BASE_ID = 255 * 255 * 254
staticinherited

◆ CONSTRAINED_COLOR

ColorRGBA Slic3r::GUI::GLGizmoBase::CONSTRAINED_COLOR = {0.5f, 0.5f, 0.5f, 1.0f}
staticinherited

◆ count

◆ CursorHeightMax

float Slic3r::GUI::GLGizmoPainterBase::CursorHeightMax = 8.f
staticconstexprprotectedinherited

◆ CursorHeightMin

float Slic3r::GUI::GLGizmoPainterBase::CursorHeightMin = 0.1f
staticconstexprprotectedinherited

◆ CursorHeightStep

float Slic3r::GUI::GLGizmoPainterBase::CursorHeightStep = 0.2f
staticconstexprprotectedinherited

Referenced by get_cursor_height_step().

◆ CursorRadiusMax

◆ CursorRadiusMin

◆ CursorRadiusStep

float Slic3r::GUI::GLGizmoPainterBase::CursorRadiusStep = 0.2f
staticconstexprprotectedinherited

Referenced by get_cursor_radius_step().

◆ DEFAULT_BASE_COLOR

ColorRGBA Slic3r::GUI::GLGizmoBase::DEFAULT_BASE_COLOR = { 0.625f, 0.625f, 0.625f, 1.0f }
staticinherited

◆ DEFAULT_DRAG_COLOR

ColorRGBA Slic3r::GUI::GLGizmoBase::DEFAULT_DRAG_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }
staticinherited

◆ DEFAULT_HIGHLIGHT_COLOR

ColorRGBA Slic3r::GUI::GLGizmoBase::DEFAULT_HIGHLIGHT_COLOR = {1.0f, 0.38f, 0.0f, 1.0f}
staticinherited

◆ FLATTEN_COLOR

ColorRGBA Slic3r::GUI::GLGizmoBase::FLATTEN_COLOR = {0.96f, 0.93f, 0.93f, 0.5f}
staticinherited

◆ FLATTEN_HOVER_COLOR

ColorRGBA Slic3r::GUI::GLGizmoBase::FLATTEN_HOVER_COLOR = {1.0f, 1.0f, 1.0f, 0.75f}
staticinherited

◆ GRABBER_ELEMENTS_MAX_COUNT

const unsigned int Slic3r::GUI::GLGizmoBase::GRABBER_ELEMENTS_MAX_COUNT = 7
staticinherited

◆ GRABBER_HOVER_COL

ColorRGBA Slic3r::GUI::GLGizmoBase::GRABBER_HOVER_COL = {0.863f, 0.125f, 0.063f, 1.0f}
staticinherited

◆ GRABBER_NORMAL_COL

ColorRGBA Slic3r::GUI::GLGizmoBase::GRABBER_NORMAL_COL = {1.0f, 1.0f, 1.0f, 1.0f}
staticinherited

◆ GRABBER_UNIFORM_COL

ColorRGBA Slic3r::GUI::GLGizmoBase::GRABBER_UNIFORM_COL = {0, 1.0, 1.0, 1.0f}
staticinherited

◆ GRABBER_UNIFORM_HOVER_COL

ColorRGBA Slic3r::GUI::GLGizmoBase::GRABBER_UNIFORM_HOVER_COL = {0, 0.7, 0.7, 1.0f}
staticinherited

◆ INV_ZOOM

◆ last_input_window_width

float Slic3r::GUI::GLGizmoBase::last_input_window_width = 0
protectedinherited

◆ m_active_layer_slot

◆ m_adjust

◆ m_adjust_anchor_normal

Vec3f Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_anchor_normal = Vec3f::UnitZ()
private

◆ m_adjust_anchor_pos

Vec3f Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_anchor_pos = Vec3f::Zero()
private

◆ m_adjust_anchor_valid

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_anchor_valid = false
private

◆ m_adjust_applied

TextureAdjustments Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_applied
private

◆ m_adjust_arrow_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_arrow_glmodel
private

◆ m_adjust_drag_handle

AdjustHandle Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_drag_handle = AdjustHandle::None
private

◆ m_adjust_drag_start_offset

Vec2f Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_drag_start_offset = Vec2f::Zero()
private

Referenced by on_mouse_adjust_texture().

◆ m_adjust_drag_start_planar

Vec2f Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_drag_start_planar = Vec2f::Zero()
private

Referenced by on_mouse_adjust_texture().

◆ m_adjust_panel_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_panel_glmodel
private

◆ m_adjust_preview_job

std::future<DecodedHeightTexture> Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_preview_job
private

◆ m_adjust_preview_running

TextureAdjustments Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_preview_running
private

◆ m_adjust_preview_shown

TextureAdjustments Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_preview_shown
private

◆ m_adjust_preview_source

DecodedHeightTexture Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_preview_source
private

◆ m_adjust_preview_tex

std::unique_ptr<GLTexture> Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_preview_tex
private

◆ m_adjust_preview_valid

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_preview_valid = false
private

◆ m_adjust_slot

int Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_slot = -1
private

◆ m_adjust_source

DecodedHeightTexture Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_source
private

◆ m_adjust_source_image

const void* Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_source_image = nullptr
private

◆ m_adjust_texture_mode

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_texture_mode = false
private

◆ m_adjust_volume_id

size_t Slic3r::GUI::GLGizmoTextureDisplacement::m_adjust_volume_id = 0
private

◆ m_auto_update

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_auto_update = true
private

◆ m_bake_in_progress

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_bake_in_progress = false
private

◆ m_button_down

ButtonButton Slic3r::GUI::GLGizmoPainterBase::m_button_down = Button::None
privateinherited

◆ m_c

CommonGizmosDataPool* Slic3r::GUI::GLGizmoBase::m_c { nullptr }
protectedinherited
155{ nullptr };

Referenced by Slic3r::GUI::GLGizmoTextureDisplacement::apply_projection_frame(), Slic3r::GUI::GLGizmoTextureDisplacement::bake(), Slic3r::GUI::GLGizmoTextureDisplacement::capture_view_projection(), Slic3r::GUI::GLGizmoCut3D::check_and_update_connectors_state(), Slic3r::GUI::GLGizmoBrimEars::data_changed(), Slic3r::GUI::GLGizmoCut3D::data_changed(), Slic3r::GUI::GLGizmoMmuSegmentation::data_changed(), Slic3r::GUI::GLGizmoPainterBase::data_changed(), Slic3r::GUI::GLGizmoCut3D::dragging_connector(), Slic3r::GUI::GLGizmoMeshBoolean::generate_new_volume(), Slic3r::GUI::GLGizmoPainterBase::get_clipping_plane_data(), Slic3r::GUI::GLGizmoPainterBase::get_clipping_plane_in_volume_coordinates(), Slic3r::GUI::GLGizmoBrimEars::get_config_options(), Slic3r::GUI::GLGizmoPainterBase::get_projected_height_range(), Slic3r::GUI::GLGizmoPainterBase::get_projected_mouse_positions(), Slic3r::GUI::GLGizmoFdmSupports::get_selection_support_threshold_angle(), Slic3r::GUI::GLGizmoBrimEars::gizmo_event(), Slic3r::GUI::GLGizmoCut3D::gizmo_event(), Slic3r::GUI::GLGizmoMeshBoolean::gizmo_event(), Slic3r::GUI::GLGizmoPainterBase::gizmo_event(), Slic3r::GUI::GLGizmoCut3D::has_valid_contour(), Slic3r::GUI::GLGizmoSimplify::init_model(), Slic3r::GUI::GLGizmoMmuSegmentation::init_model_triangle_selectors(), Slic3r::GUI::GLGizmoFdmSupports::init_print_instance(), Slic3r::GUI::GLGizmoBrimEars::is_mesh_point_clipped(), Slic3r::GUI::GLGizmoPainterBase::is_mesh_point_clipped(), Slic3r::GUI::GLGizmoCut3D::is_outside_of_cut_contour(), Slic3r::GUI::GLGizmoFlatten::is_plane_update_necessary(), Slic3r::GUI::GLGizmoPainterBase::mesh_trafo_matrices(), Slic3r::GUI::GLGizmoFdmSupports::need_regenerate_support_volumes(), Slic3r::GUI::GLGizmoMeshBoolean::on_get_requirements(), Slic3r::GUI::GLGizmoMeshBoolean::on_load(), Slic3r::GUI::GLGizmoTextureDisplacement::on_mouse_adjust_texture(), Slic3r::GUI::GLGizmoCut3D::on_register_raycasters_for_picking(), Slic3r::GUI::GLGizmoBrimEars::on_render(), Slic3r::GUI::GLGizmoMeshBoolean::on_render(), Slic3r::GUI::GLGizmoBrimEars::on_render_input_window(), Slic3r::GUI::GLGizmoCut3D::on_render_input_window(), Slic3r::GUI::GLGizmoFdmSupports::on_render_input_window(), Slic3r::GUI::GLGizmoFuzzySkin::on_render_input_window(), Slic3r::GUI::GLGizmoMmuSegmentation::on_render_input_window(), Slic3r::GUI::GLGizmoSeam::on_render_input_window(), Slic3r::GUI::GLGizmoTextureDisplacement::on_render_input_window(), Slic3r::GUI::GLGizmoCut3D::on_set_state(), Slic3r::GUI::GLGizmoFdmSupports::on_set_state(), Slic3r::GUI::GLGizmoMmuSegmentation::on_set_state(), Slic3r::GUI::GLGizmoSeam::on_set_state(), Slic3r::GUI::GLGizmoTextureDisplacement::preview_part(), Slic3r::GUI::GLGizmoCut3D::process_contours(), Slic3r::GUI::GLGizmoCut3D::process_selection_rectangle(), Slic3r::GUI::GLGizmoCut3D::put_connectors_on_cut_plane(), Slic3r::GUI::GLGizmoTextureDisplacement::queue_prepare(), Slic3r::GUI::GLGizmoBrimEars::reload_cache(), Slic3r::GUI::GLGizmoMmuSegmentation::remap_filament_assignments(), Slic3r::GUI::GLGizmoTextureDisplacement::render_adjust_texture_gizmo(), Slic3r::GUI::GLGizmoCut3D::render_clipper_cut(), Slic3r::GUI::GLGizmoCut3D::render_connectors(), Slic3r::GUI::GLGizmoCut3D::render_debug_input_window(), Slic3r::GUI::GLGizmoPainterBase::render_follows_cursor(), Slic3r::GUI::GLGizmoTextureDisplacement::render_island_overlay(), Slic3r::GUI::GLGizmoTextureDisplacement::render_paint_overlay(), Slic3r::GUI::GLGizmoTextureDisplacement::render_painted_colors(), Slic3r::GUI::GLGizmoFdmSupports::render_painter_gizmo(), Slic3r::GUI::GLGizmoFuzzySkin::render_painter_gizmo(), Slic3r::GUI::GLGizmoMmuSegmentation::render_painter_gizmo(), Slic3r::GUI::GLGizmoSeam::render_painter_gizmo(), Slic3r::GUI::GLGizmoTextureDisplacement::render_painter_gizmo(), Slic3r::GUI::GLGizmoBrimEars::render_points(), Slic3r::GUI::GLGizmoTextureDisplacement::render_preview_mesh(), Slic3r::GUI::GLGizmoTextureDisplacement::render_seam_overlay(), Slic3r::GUI::GLGizmoTextureDisplacement::render_shaded_preview_mesh(), Slic3r::GUI::GLGizmoTextureDisplacement::render_subdivide_preview(), Slic3r::GUI::GLGizmoPainterBase::render_triangles(), Slic3r::GUI::GLGizmoTextureDisplacement::render_uvcheck_mesh(), Slic3r::GUI::GLGizmoTextureDisplacement::render_wireframe_overlay(), Slic3r::GUI::GLGizmoCut3D::reset_connectors(), Slic3r::GUI::GLGizmoFdmSupports::run_thread(), Slic3r::GUI::GLGizmoTextureDisplacement::seam_edge_at(), Slic3r::GUI::GLGizmoTextureDisplacement::seam_vertex_at(), Slic3r::GUI::GLGizmoFdmSupports::select_facets_by_angle(), Slic3r::GUI::GLGizmoTextureDisplacement::select_visible_faces(), Slic3r::GUI::GLGizmoTextureDisplacement::select_whole_model(), Slic3r::GUI::GLGizmoBrimEars::set_brim_data(), set_common_data_pool(), Slic3r::GUI::GLGizmoCut3D::set_connectors_editing(), Slic3r::GUI::GLGizmoTextureDisplacement::shaded_preview_ready(), Slic3r::GUI::GLGizmoTextureDisplacement::smooth_model(), Slic3r::GUI::GLGizmoTextureDisplacement::subdivide_model(), Slic3r::GUI::GLGizmoTextureDisplacement::swap_layer_slots(), Slic3r::GUI::GLGizmoCut3D::switch_to_mode(), Slic3r::GUI::GLGizmoTextureDisplacement::texture_parts(), Slic3r::GUI::GLGizmoTextureDisplacement::texture_volume(), Slic3r::GUI::GLGizmoTextureDisplacement::texture_volume_raycaster_index(), Slic3r::GUI::GLGizmoCut3D::unproject_on_cut_plane(), Slic3r::GUI::GLGizmoBrimEars::unproject_on_mesh(), Slic3r::GUI::GLGizmoBrimEars::unproject_on_mesh2(), Slic3r::GUI::GLGizmoCut3D::update_bb(), Slic3r::GUI::GLGizmoCut3D::update_clipper(), Slic3r::GUI::GLGizmoFdmSupports::update_from_model_object(), Slic3r::GUI::GLGizmoFuzzySkin::update_from_model_object(), Slic3r::GUI::GLGizmoSeam::update_from_model_object(), Slic3r::GUI::GLGizmoTextureDisplacement::update_from_model_object(), Slic3r::GUI::GLGizmoBrimEars::update_model_object(), Slic3r::GUI::GLGizmoFdmSupports::update_model_object(), Slic3r::GUI::GLGizmoFuzzySkin::update_model_object(), Slic3r::GUI::GLGizmoMmuSegmentation::update_model_object(), Slic3r::GUI::GLGizmoSeam::update_model_object(), Slic3r::GUI::GLGizmoTextureDisplacement::update_model_object(), Slic3r::GUI::GLGizmoFlatten::update_planes(), Slic3r::GUI::GLGizmoPainterBase::update_raycast_cache(), and Slic3r::GUI::GLGizmoCut3D::update_raycasters_for_picking_transform().

◆ m_circle

GLModel Slic3r::GUI::GLGizmoPainterBase::m_circle
protectedinherited

Referenced by render_cursor_circle().

◆ m_color_tex

std::unique_ptr<GLTexture> Slic3r::GUI::GLGizmoTextureDisplacement::m_color_tex
private

Referenced by get_layer_color_texture().

◆ m_color_tex_smoothing

float Slic3r::GUI::GLGizmoTextureDisplacement::m_color_tex_smoothing = -1.f
private

Referenced by get_layer_color_texture().

◆ m_color_tex_source

std::weak_ptr<const std::vector<unsigned char> > Slic3r::GUI::GLGizmoTextureDisplacement::m_color_tex_source
private

◆ m_cursor_height

float Slic3r::GUI::GLGizmoPainterBase::m_cursor_height = 0.2f
protectedinherited

◆ m_cursor_radius

◆ m_cursor_type

◆ m_cursor_z

float Slic3r::GUI::GLGizmoPainterBase::m_cursor_z {0}
mutableprivateinherited
386{0};

◆ m_cut_contours

std::vector<CutContours> Slic3r::GUI::GLGizmoPainterBase::m_cut_contours
mutableprivateinherited

◆ m_debug_check_topology

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_debug_check_topology = true
private

◆ m_debug_in_progress

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_debug_in_progress = false
private

◆ m_debug_stage

int Slic3r::GUI::GLGizmoTextureDisplacement::m_debug_stage = -1
private

◆ m_debug_stages

std::vector<BakeStageSnapshot> Slic3r::GUI::GLGizmoTextureDisplacement::m_debug_stages
private

◆ m_desc

std::map<std::string, wxString> Slic3r::GUI::GLGizmoTextureDisplacement::m_desc
private

Referenced by on_init(), and on_render_input_window().

◆ m_dirty

bool Slic3r::GUI::GLGizmoBase::m_dirty { false }
privateinherited
293{ false };

Referenced by set_dirty(), and update_items_state().

◆ m_dragging

◆ m_erase_mode

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_erase_mode = false
private

◆ m_first_input_window_render

bool Slic3r::GUI::GLGizmoBase::m_first_input_window_render { true }
protectedinherited
154{ true };

Referenced by render_input_window().

◆ m_grabbers

◆ m_group_id

int Slic3r::GUI::GLGizmoBase::m_group_id
protectedinherited

◆ m_height_start_pos

Vec2i32 Slic3r::GUI::GLGizmoPainterBase::m_height_start_pos {0, 0}
mutableprivateinherited
389{0, 0};

◆ m_height_start_z_in_imgui

double Slic3r::GUI::GLGizmoPainterBase::m_height_start_z_in_imgui {0}
mutableprivateinherited
387{0};

◆ m_height_tex

std::unique_ptr<GLTexture> Slic3r::GUI::GLGizmoTextureDisplacement::m_height_tex
private

◆ m_height_tex_smoothing

float Slic3r::GUI::GLGizmoTextureDisplacement::m_height_tex_smoothing = -1.f
private

◆ m_height_tex_source

std::weak_ptr<const std::vector<unsigned char> > Slic3r::GUI::GLGizmoTextureDisplacement::m_height_tex_source
private

◆ m_highlight_by_angle_threshold_deg

◆ m_horizontal_only

bool Slic3r::GUI::GLGizmoPainterBase::m_horizontal_only = false
protectedinherited

◆ m_hover_id

int Slic3r::GUI::GLGizmoBase::m_hover_id { -1 }
protectedinherited
150{ -1 };

Referenced by Slic3r::GUI::GLGizmoScale3D::calc_ratio(), Slic3r::GUI::GLGizmoScale3D::do_scale_along_axis(), Slic3r::GUI::GLGizmoScale3D::do_scale_uniform(), Slic3r::GUI::GLGizmoCut3D::dragging_connector(), Slic3r::GUI::GLGizmoCut3D::dragging_grabber_move(), Slic3r::GUI::GLGizmoCut3D::dragging_grabber_rotation(), get_hover_id(), Slic3r::GUI::GLGizmoCut3D::get_tooltip(), Slic3r::GUI::GLGizmoMove3D::get_tooltip(), Slic3r::GUI::GLGizmoRotate::get_tooltip(), Slic3r::GUI::GLGizmoScale3D::get_tooltip(), Slic3r::GUI::GLGizmoBrimEars::gizmo_event(), Slic3r::GUI::GLGizmoCut3D::gizmo_event(), Slic3r::GUI::GLGizmoCut3D::is_selection_changed(), Slic3r::GUI::GLGizmoBrimEars::on_dragging(), Slic3r::GUI::GLGizmoCut3D::on_dragging(), Slic3r::GUI::GLGizmoMove3D::on_dragging(), Slic3r::GUI::GLGizmoRotate3D::on_dragging(), Slic3r::GUI::GLGizmoScale3D::on_dragging(), Slic3r::GUI::GLGizmoCut3D::on_mouse(), Slic3r::GUI::GLGizmoFlatten::on_mouse(), Slic3r::GUI::GLGizmoMeasure::on_mouse(), Slic3r::GUI::GLGizmoFlatten::on_render(), Slic3r::GUI::GLGizmoMeasure::on_render(), Slic3r::GUI::GLGizmoMove3D::on_render(), Slic3r::GUI::GLGizmoRotate3D::on_render(), Slic3r::GUI::GLGizmoRotate::on_render(), Slic3r::GUI::GLGizmoScale3D::on_render(), Slic3r::GUI::GLGizmoBrimEars::on_set_hover_id(), Slic3r::GUI::GLGizmoEmboss::on_set_hover_id(), Slic3r::GUI::GLGizmoRotate3D::on_set_hover_id(), Slic3r::GUI::GLGizmoSVG::on_set_hover_id(), Slic3r::GUI::GLGizmoMeasure::on_set_state(), Slic3r::GUI::GLGizmoBrimEars::on_start_dragging(), Slic3r::GUI::GLGizmoCut3D::on_start_dragging(), Slic3r::GUI::GLGizmoMove3D::on_start_dragging(), Slic3r::GUI::GLGizmoRotate3D::on_start_dragging(), Slic3r::GUI::GLGizmoScale3D::on_start_dragging(), Slic3r::GUI::GLGizmoBrimEars::on_stop_dragging(), Slic3r::GUI::GLGizmoCut3D::on_stop_dragging(), Slic3r::GUI::GLGizmoRotate3D::on_stop_dragging(), Slic3r::GUI::GLGizmoCut3D::render_connectors(), Slic3r::GUI::GLGizmoCut3D::render_cut_plane_grabbers(), Slic3r::GUI::GLGizmoCut3D::render_flip_plane_button(), render_grabbers(), Slic3r::GUI::GLGizmoBrimEars::render_points(), set_hover_id(), Slic3r::GUI::GLGizmoMeasure::show_selection_ui(), Slic3r::GUI::GLGizmoScale3D::update_grabbers_data(), and use_grabbers().

◆ m_icon_filename

std::string Slic3r::GUI::GLGizmoBase::m_icon_filename
protectedinherited

◆ m_imgui

ImGuiWrapper* Slic3r::GUI::GLGizmoBase::m_imgui
protectedinherited

Referenced by GLGizmoBase(), Slic3r::GUI::GLGizmoEmboss::GLGizmoEmboss(), Slic3r::GUI::GLGizmoCut3D::add_horizontal_scaled_interval(), Slic3r::GUI::GLGizmoCut3D::add_vertical_scaled_interval(), Slic3r::GUI::GLGizmoSimplify::create_gui_cfg(), Slic3r::GUI::GLGizmoEmboss::draw_advanced(), Slic3r::GUI::GLGizmoEmboss::draw_bold_button(), Slic3r::GUI::GLGizmoEmboss::draw_delete_style_button(), Slic3r::GUI::GLGizmoSVG::draw_depth(), Slic3r::GUI::GLGizmoSVG::draw_distance(), Slic3r::GUI::GLGizmoSVG::draw_filename(), Slic3r::GUI::GLGizmoEmboss::draw_font_list(), Slic3r::GUI::GLGizmoEmboss::draw_font_list_line(), Slic3r::GUI::GLGizmoEmboss::draw_italic_button(), Slic3r::GUI::GLGizmoSVG::draw_mirroring(), Slic3r::GUI::GLGizmoEmboss::draw_model_type(), Slic3r::GUI::GLGizmoSVG::draw_model_type(), Slic3r::GUI::GLGizmoSVG::draw_rotation(), Slic3r::GUI::GLGizmoSVG::draw_size(), Slic3r::GUI::GLGizmoEmboss::draw_style_add_button(), Slic3r::GUI::GLGizmoEmboss::draw_style_list(), Slic3r::GUI::GLGizmoEmboss::draw_style_rename_button(), Slic3r::GUI::GLGizmoEmboss::draw_style_rename_popup(), Slic3r::GUI::GLGizmoEmboss::draw_style_save_as_popup(), Slic3r::GUI::GLGizmoEmboss::draw_style_save_button(), Slic3r::GUI::GLGizmoEmboss::draw_text_input(), Slic3r::GUI::GLGizmoSVG::draw_use_surface(), Slic3r::GUI::GLGizmoEmboss::draw_window(), GizmoImguiBegin(), GizmoImguiEnd(), GizmoImguiSetNextWIndowPos(), Slic3r::GUI::GLGizmoCut3D::init_input_window_data(), Slic3r::GUI::GLGizmoMeasure::on_mouse(), Slic3r::GUI::GLGizmoAssembly::on_render_input_window(), Slic3r::GUI::GLGizmoBrimEars::on_render_input_window(), Slic3r::GUI::GLGizmoEmboss::on_render_input_window(), Slic3r::GUI::GLGizmoFdmSupports::on_render_input_window(), Slic3r::GUI::GLGizmoFuzzySkin::on_render_input_window(), Slic3r::GUI::GLGizmoMeasure::on_render_input_window(), Slic3r::GUI::GLGizmoMeshBoolean::on_render_input_window(), Slic3r::GUI::GLGizmoMmuSegmentation::on_render_input_window(), Slic3r::GUI::GLGizmoMove3D::on_render_input_window(), Slic3r::GUI::GLGizmoRotate3D::on_render_input_window(), Slic3r::GUI::GLGizmoScale3D::on_render_input_window(), Slic3r::GUI::GLGizmoSeam::on_render_input_window(), Slic3r::GUI::GLGizmoSimplify::on_render_input_window(), Slic3r::GUI::GLGizmoSVG::on_render_input_window(), Slic3r::GUI::GLGizmoTextureDisplacement::on_render_input_window(), Slic3r::GUI::GLGizmoTextureDisplacement::panel_tooltip(), Slic3r::GUI::GLGizmoCut3D::render_angle_input(), Slic3r::GUI::GLGizmoCut3D::render_build_size(), Slic3r::GUI::GLGizmoCut3D::render_color_marker(), render_combo(), Slic3r::GUI::GLGizmoCut3D::render_connectors_input_window(), Slic3r::GUI::GLGizmoCut3D::render_cut_mode_combo(), Slic3r::GUI::GLGizmoCut3D::render_cut_plane_input_window(), Slic3r::GUI::GLGizmoCut3D::render_debug_input_window(), Slic3r::GUI::GLGizmoTextureDisplacement::render_debug_stage_panel(), Slic3r::GUI::GLGizmoMeasure::render_dimensioning(), Slic3r::GUI::GLGizmoCut3D::render_double_input(), Slic3r::GUI::GLGizmoMmuSegmentation::render_filament_remap_ui(), Slic3r::GUI::GLGizmoCut3D::render_flip_plane_button(), Slic3r::GUI::GLGizmoCut3D::render_groove_float_input(), Slic3r::GUI::GLGizmoCut3D::render_groove_int_input(), Slic3r::GUI::GLGizmoCut3D::render_groove_two_float_input(), Slic3r::GUI::GLGizmoTextureDisplacement::render_image_adjust_window(), Slic3r::GUI::GLGizmoAssembly::render_input_window_warning(), Slic3r::GUI::GLGizmoCut3D::render_input_window_warning(), Slic3r::GUI::GLGizmoCut3D::render_move_center_input(), Slic3r::GUI::GLGizmoCut3D::render_reset_button(), Slic3r::GUI::GLGizmoCut3D::render_slider_input(), Slic3r::GUI::GLGizmoCut3D::render_slider_two_input(), Slic3r::GUI::GLGizmoCut3D::render_snap_specific_input(), Slic3r::GUI::GLGizmoTextureDisplacement::render_texture_library_popup(), Slic3r::GUI::GLGizmoTextureDisplacement::render_tip_media(), Slic3r::GUI::GLGizmoCut3D::render_tooltip_button(), Slic3r::GUI::GLGizmoFdmSupports::render_tooltip_button(), Slic3r::GUI::GLGizmoFuzzySkin::render_tooltip_button(), Slic3r::GUI::GLGizmoMmuSegmentation::render_tooltip_button(), Slic3r::GUI::GLGizmoPainterBase::render_triangles(), Slic3r::GUI::GLGizmoEmboss::rev_checkbox(), Slic3r::GUI::GLGizmoEmboss::rev_input(), Slic3r::GUI::GLGizmoEmboss::rev_slider(), Slic3r::GUI::GLGizmoEmboss::rev_slider(), Slic3r::GUI::GLGizmoEmboss::rev_slider(), Slic3r::GUI::GLGizmoEmboss::revertible(), Slic3r::GUI::GLGizmoMeasure::show_distance_xyz_ui(), Slic3r::GUI::GLGizmoMeasure::show_face_face_assembly_common(), Slic3r::GUI::GLGizmoMeasure::show_face_face_assembly_senior(), and Slic3r::GUI::GLGizmoMeasure::show_selection_ui().

◆ m_internal_stack_active

bool Slic3r::GUI::GLGizmoPainterBase::m_internal_stack_active = false
privateinherited

◆ m_is_cursor_in_imgui

bool Slic3r::GUI::GLGizmoPainterBase::m_is_cursor_in_imgui {false}
mutableprivateinherited
390{false};

◆ m_is_dark_mode

◆ m_is_set_height_start_z_by_imgui

bool Slic3r::GUI::GLGizmoPainterBase::m_is_set_height_start_z_by_imgui {false}
mutableprivateinherited
388{false};

◆ m_island_drag_active

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_island_drag_active = false
private

Referenced by on_island_edited(), and on_shutdown().

◆ m_island_move_set

std::vector<int> Slic3r::GUI::GLGizmoTextureDisplacement::m_island_move_set
private

◆ m_island_overlay_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_island_overlay_glmodel
private

◆ m_island_overlay_selection

std::vector<int> Slic3r::GUI::GLGizmoTextureDisplacement::m_island_overlay_selection
private

◆ m_island_view

IslandViewFollow Slic3r::GUI::GLGizmoTextureDisplacement::m_island_view
private

◆ m_last_mouse_click

Vec2d Slic3r::GUI::GLGizmoPainterBase::m_last_mouse_click = Vec2d::Zero()
privateinherited

◆ m_layer_expanded

std::array<bool, TEXTURE_DISPLACEMENT_MAX_LAYERS> Slic3r::GUI::GLGizmoTextureDisplacement::m_layer_expanded {}
private

◆ m_library_generation

uint64_t Slic3r::GUI::GLGizmoTextureDisplacement::m_library_generation = 0
private

◆ m_library_textures

std::map<std::string, LibraryTexture> Slic3r::GUI::GLGizmoTextureDisplacement::m_library_textures
private

◆ m_mix_ranking

std::optional<std::vector<PaletteEntry> > Slic3r::GUI::GLGizmoTextureDisplacement::m_mix_ranking

Referenced by cached_palette(), and on_shutdown().

◆ m_old_center

Vec2d Slic3r::GUI::GLGizmoPainterBase::m_old_center { Vec2d::Zero() }
protectedinherited
308{ Vec2d::Zero() };

Referenced by render_cursor_circle().

◆ m_old_cursor_radius

float Slic3r::GUI::GLGizmoPainterBase::m_old_cursor_radius { 0.0f }
protectedinherited
309{ 0.0f };

Referenced by render_cursor_circle().

◆ m_old_mo_id

ObjectID Slic3r::GUI::GLGizmoPainterBase::m_old_mo_id
privateinherited

Referenced by data_changed(), and on_set_state().

◆ m_old_state

EState Slic3r::GUI::GLGizmoPainterBase::m_old_state = Off
privateinherited

Referenced by on_set_state().

◆ m_old_volumes_size

size_t Slic3r::GUI::GLGizmoPainterBase::m_old_volumes_size = 0
privateinherited

Referenced by data_changed().

◆ m_other_paint_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_other_paint_glmodel
private

◆ m_other_paint_key

std::string Slic3r::GUI::GLGizmoTextureDisplacement::m_other_paint_key
private

◆ m_paint_on_overhangs_only

bool Slic3r::GUI::GLGizmoPainterBase::m_paint_on_overhangs_only = false
protectedinherited

◆ m_paint_overlay_dirty

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_paint_overlay_dirty = false
private

◆ m_paint_overlay_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_paint_overlay_glmodel
private

◆ m_painted_area_key

std::string Slic3r::GUI::GLGizmoTextureDisplacement::m_painted_area_key
private

Referenced by painted_area_mm2().

◆ m_painted_area_mm2

double Slic3r::GUI::GLGizmoTextureDisplacement::m_painted_area_mm2 = 0.
private

Referenced by painted_area_mm2().

◆ m_painted_colors

PaintedColors Slic3r::GUI::GLGizmoTextureDisplacement::m_painted_colors
private

◆ m_painted_colors_drawn_key

std::string Slic3r::GUI::GLGizmoTextureDisplacement::m_painted_colors_drawn_key
private

◆ m_painted_colors_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_painted_colors_glmodel
private

◆ m_painted_colors_key

std::string Slic3r::GUI::GLGizmoTextureDisplacement::m_painted_colors_key
private

◆ m_painted_colors_runs

std::vector<std::pair<int, std::pair<size_t, size_t> > > Slic3r::GUI::GLGizmoTextureDisplacement::m_painted_colors_runs
private

◆ m_palette_cache

std::vector<PaletteEntry> Slic3r::GUI::GLGizmoTextureDisplacement::m_palette_cache

◆ m_palette_changed

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_palette_changed = false

◆ m_palette_filaments

std::vector<ColorRGBA> Slic3r::GUI::GLGizmoTextureDisplacement::m_palette_filaments

◆ m_palette_free_slots

int Slic3r::GUI::GLGizmoTextureDisplacement::m_palette_free_slots = 0

Referenced by cached_palette().

◆ m_palette_images

std::vector<std::shared_ptr<std::vector<unsigned char> > > Slic3r::GUI::GLGizmoTextureDisplacement::m_palette_images

Referenced by cached_palette(), and on_shutdown().

◆ m_palette_mix_count

int Slic3r::GUI::GLGizmoTextureDisplacement::m_palette_mix_count = 0

Referenced by cached_palette().

◆ m_palette_mixing

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_palette_mixing = false

Referenced by cached_palette().

◆ m_palette_pure_quantizer

ColorQuantizeFn Slic3r::GUI::GLGizmoTextureDisplacement::m_palette_pure_quantizer

◆ m_palette_quantizer

ColorQuantizeFn Slic3r::GUI::GLGizmoTextureDisplacement::m_palette_quantizer

◆ m_palette_slots

std::vector<std::string> Slic3r::GUI::GLGizmoTextureDisplacement::m_palette_slots

Referenced by cached_palette(), and on_shutdown().

◆ m_panel_body_h

float Slic3r::GUI::GLGizmoTextureDisplacement::m_panel_body_h = 0.f
private

Referenced by on_render_input_window().

◆ m_panel_footer_h

float Slic3r::GUI::GLGizmoTextureDisplacement::m_panel_footer_h = 0.f
private

Referenced by on_render_input_window().

◆ m_panel_icon_map

std::map<std::string, IconManager::Icons> Slic3r::GUI::GLGizmoTextureDisplacement::m_panel_icon_map
private

◆ m_panel_icons

IconManager Slic3r::GUI::GLGizmoTextureDisplacement::m_panel_icons
private

Referenced by ensure_panel_icons().

◆ m_panel_icons_tried

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_panel_icons_tried = false
private

Referenced by ensure_panel_icons().

◆ m_panel_mode

int Slic3r::GUI::GLGizmoTextureDisplacement::m_panel_mode = 0
private

◆ m_panel_scroll_applied

float Slic3r::GUI::GLGizmoTextureDisplacement::m_panel_scroll_applied = -1.f
private

Referenced by on_render_input_window().

◆ m_panel_scroll_target

float Slic3r::GUI::GLGizmoTextureDisplacement::m_panel_scroll_target = 0.f
private

Referenced by on_render_input_window().

◆ m_parent

GLCanvas3D& Slic3r::GUI::GLGizmoBase::m_parent
protectedinherited

Referenced by GLGizmoBase(), Slic3r::GUI::GLGizmoCut3D::add_connector(), Slic3r::GUI::GLGizmoTextureDisplacement::add_texture_layer(), Slic3r::GUI::GLGizmoCut3D::apply_color_clip_plane_colors(), Slic3r::GUI::GLGizmoTextureDisplacement::apply_projection_frame(), Slic3r::GUI::GLGizmoSimplify::apply_simplify(), Slic3r::GUI::GLGizmoTextureDisplacement::apply_view_mode(), Slic3r::GUI::GLGizmoBrimEars::auto_generate(), Slic3r::GUI::GLGizmoTextureDisplacement::bake(), Slic3r::GUI::GLGizmoCut3D::bounding_box(), Slic3r::GUI::GLGizmoPainterBase::bounding_box(), Slic3r::GUI::GLGizmoEmboss::calculate_scale(), Slic3r::GUI::GLGizmoSVG::calculate_scale(), Slic3r::GUI::GLGizmoTextureDisplacement::capture_view_projection(), Slic3r::GUI::GLGizmoMove3D::change_cs_by_selection(), Slic3r::GUI::GLGizmoScale3D::change_cs_by_selection(), Slic3r::GUI::GLGizmoEmboss::close(), Slic3r::GUI::GLGizmoSimplify::close(), Slic3r::GUI::GLGizmoSVG::close(), Slic3r::GUI::GLGizmoTextureDisplacement::close_image_adjust(), Slic3r::GUI::GLGizmoEmboss::create_volume(), Slic3r::GUI::GLGizmoEmboss::create_volume(), Slic3r::GUI::GLGizmoSVG::create_volume(), Slic3r::GUI::GLGizmoSVG::create_volume(), Slic3r::GUI::GLGizmoSVG::create_volume(), Slic3r::GUI::GLGizmoSVG::create_volume(), Slic3r::GUI::GLGizmoFlatten::data_changed(), Slic3r::GUI::GLGizmoMeasure::data_changed(), Slic3r::GUI::GLGizmoMove3D::data_changed(), Slic3r::GUI::GLGizmoPainterBase::data_changed(), Slic3r::GUI::GLGizmoRotate3D::data_changed(), Slic3r::GUI::GLGizmoScale3D::data_changed(), Slic3r::GUI::GLGizmoCut3D::delete_selected_connectors(), Slic3r::GUI::GLGizmoEmboss::do_mirror(), do_stop_dragging(), Slic3r::GUI::GLGizmoEmboss::draw_advanced(), Slic3r::GUI::GLGizmoSVG::draw_distance(), Slic3r::GUI::GLGizmoSVG::draw_face_the_camera(), Slic3r::GUI::GLGizmoSVG::draw_filename(), Slic3r::GUI::GLGizmoSVG::draw_mirroring(), Slic3r::GUI::GLGizmoEmboss::draw_model_type(), Slic3r::GUI::GLGizmoSVG::draw_model_type(), Slic3r::GUI::GLGizmoSVG::draw_rotation(), Slic3r::GUI::GLGizmoSVG::draw_size(), Slic3r::GUI::GLGizmoEmboss::draw_style_list(), Slic3r::GUI::GLGizmoEmboss::draw_window(), Slic3r::GUI::GLGizmoTextureDisplacement::ease_wheel_scroll(), Slic3r::GUI::GLGizmoBrimEars::find_single(), Slic3r::GUI::GLGizmoBrimEars::first_layer_slicer(), Slic3r::GUI::GLGizmoMeshBoolean::generate_new_volume(), Slic3r::GUI::GLGizmoPainterBase::get_projected_height_range(), Slic3r::GUI::GLGizmoMove3D::get_tooltip(), Slic3r::GUI::GLGizmoScale3D::get_tooltip(), Slic3r::GUI::GLGizmoBrimEars::gizmo_event(), Slic3r::GUI::GLGizmoMeasure::gizmo_event(), Slic3r::GUI::GLGizmoMeshBoolean::gizmo_event(), Slic3r::GUI::GLGizmoPainterBase::gizmo_event(), GizmoImguiSetNextWIndowPos(), Slic3r::GUI::GLGizmoCut3D::has_valid_groove(), Slic3r::GUI::GLGizmoSimplify::init_model(), Slic3r::GUI::GLGizmoMeasure::init_plane_glmodel(), Slic3r::GUI::GLGizmoFdmSupports::init_print_instance(), Slic3r::GUI::GLGizmoPainterBase::mesh_trafo_matrices(), Slic3r::GUI::GLGizmoTextureDisplacement::move_texture_layer(), Slic3r::GUI::GLGizmoMove3D::on_dragging(), Slic3r::GUI::GLGizmoAssembly::on_is_activable(), Slic3r::GUI::GLGizmoBrimEars::on_is_activable(), Slic3r::GUI::GLGizmoCut3D::on_is_activable(), Slic3r::GUI::GLGizmoFlatten::on_is_activable(), Slic3r::GUI::GLGizmoMeasure::on_is_activable(), Slic3r::GUI::GLGizmoMeshBoolean::on_is_activable(), Slic3r::GUI::GLGizmoMmuSegmentation::on_is_activable(), Slic3r::GUI::GLGizmoMove3D::on_is_activable(), Slic3r::GUI::GLGizmoPainterBase::on_is_activable(), Slic3r::GUI::GLGizmoRotate3D::on_is_activable(), Slic3r::GUI::GLGizmoScale3D::on_is_activable(), Slic3r::GUI::GLGizmoSimplify::on_is_activable(), Slic3r::GUI::GLGizmoTextureDisplacement::on_is_activable(), Slic3r::GUI::GLGizmoTextureDisplacement::on_island_edited(), Slic3r::GUI::GLGizmoCut3D::on_load(), Slic3r::GUI::GLGizmoBrimEars::on_mouse(), Slic3r::GUI::GLGizmoCut3D::on_mouse(), Slic3r::GUI::GLGizmoEmboss::on_mouse(), Slic3r::GUI::GLGizmoFlatten::on_mouse(), Slic3r::GUI::GLGizmoMeasure::on_mouse(), Slic3r::GUI::GLGizmoPainterBase::on_mouse(), Slic3r::GUI::GLGizmoRotate3D::on_mouse(), Slic3r::GUI::GLGizmoScale3D::on_mouse(), Slic3r::GUI::GLGizmoSVG::on_mouse(), Slic3r::GUI::GLGizmoTextureDisplacement::on_mouse_adjust_texture(), Slic3r::GUI::GLGizmoEmboss::on_mouse_change_selection(), Slic3r::GUI::GLGizmoEmboss::on_mouse_for_rotation(), Slic3r::GUI::GLGizmoSVG::on_mouse_for_rotation(), Slic3r::GUI::GLGizmoEmboss::on_mouse_for_translate(), Slic3r::GUI::GLGizmoSVG::on_mouse_for_translate(), Slic3r::GUI::GLGizmoTextureDisplacement::on_mouse_seam(), Slic3r::GUI::GLGizmoFdmSupports::on_opening(), Slic3r::GUI::GLGizmoCut3D::on_register_raycasters_for_picking(), Slic3r::GUI::GLGizmoFlatten::on_register_raycasters_for_picking(), Slic3r::GUI::GLGizmoMove3D::on_register_raycasters_for_picking(), Slic3r::GUI::GLGizmoRotate3D::on_register_raycasters_for_picking(), Slic3r::GUI::GLGizmoScale3D::on_register_raycasters_for_picking(), Slic3r::GUI::GLGizmoBrimEars::on_render(), Slic3r::GUI::GLGizmoCut3D::on_render(), Slic3r::GUI::GLGizmoEmboss::on_render(), Slic3r::GUI::GLGizmoFlatten::on_render(), Slic3r::GUI::GLGizmoMeasure::on_render(), Slic3r::GUI::GLGizmoMeshBoolean::on_render(), Slic3r::GUI::GLGizmoMove3D::on_render(), Slic3r::GUI::GLGizmoRotate::on_render(), Slic3r::GUI::GLGizmoSimplify::on_render(), Slic3r::GUI::GLGizmoSVG::on_render(), Slic3r::GUI::GLGizmoAssembly::on_render_input_window(), Slic3r::GUI::GLGizmoBrimEars::on_render_input_window(), Slic3r::GUI::GLGizmoCut3D::on_render_input_window(), Slic3r::GUI::GLGizmoEmboss::on_render_input_window(), Slic3r::GUI::GLGizmoFdmSupports::on_render_input_window(), Slic3r::GUI::GLGizmoFuzzySkin::on_render_input_window(), Slic3r::GUI::GLGizmoMeasure::on_render_input_window(), Slic3r::GUI::GLGizmoMeshBoolean::on_render_input_window(), Slic3r::GUI::GLGizmoMmuSegmentation::on_render_input_window(), Slic3r::GUI::GLGizmoSeam::on_render_input_window(), Slic3r::GUI::GLGizmoSimplify::on_render_input_window(), Slic3r::GUI::GLGizmoSVG::on_render_input_window(), Slic3r::GUI::GLGizmoTextureDisplacement::on_render_input_window(), Slic3r::GUI::GLGizmoCut3D::on_set_state(), Slic3r::GUI::GLGizmoEmboss::on_set_state(), Slic3r::GUI::GLGizmoMeasure::on_set_state(), Slic3r::GUI::GLGizmoMmuSegmentation::on_set_state(), Slic3r::GUI::GLGizmoPainterBase::on_set_state(), Slic3r::GUI::GLGizmoSimplify::on_set_state(), Slic3r::GUI::GLGizmoSVG::on_set_state(), Slic3r::GUI::GLGizmoEmboss::on_shortcut_key(), Slic3r::GUI::GLGizmoFdmSupports::on_shutdown(), Slic3r::GUI::GLGizmoFuzzySkin::on_shutdown(), Slic3r::GUI::GLGizmoMmuSegmentation::on_shutdown(), Slic3r::GUI::GLGizmoSeam::on_shutdown(), Slic3r::GUI::GLGizmoTextureDisplacement::on_shutdown(), Slic3r::GUI::GLGizmoMove3D::on_start_dragging(), Slic3r::GUI::GLGizmoRotate::on_start_dragging(), Slic3r::GUI::GLGizmoEmboss::on_stop_dragging(), Slic3r::GUI::GLGizmoMove3D::on_stop_dragging(), Slic3r::GUI::GLGizmoRotate3D::on_stop_dragging(), Slic3r::GUI::GLGizmoScale3D::on_stop_dragging(), Slic3r::GUI::GLGizmoSVG::on_stop_dragging(), Slic3r::GUI::GLGizmoBrimEars::on_unregister_raycasters_for_picking(), Slic3r::GUI::GLGizmoCut3D::on_unregister_raycasters_for_picking(), Slic3r::GUI::GLGizmoFlatten::on_unregister_raycasters_for_picking(), Slic3r::GUI::GLGizmoMove3D::on_unregister_raycasters_for_picking(), Slic3r::GUI::GLGizmoRotate3D::on_unregister_raycasters_for_picking(), Slic3r::GUI::GLGizmoScale3D::on_unregister_raycasters_for_picking(), Slic3r::GUI::GLGizmoTextureDisplacement::on_uv_command(), Slic3r::GUI::GLGizmoTextureDisplacement::panel_tooltip(), Slic3r::GUI::GLGizmoCut3D::perform_cut(), Slic3r::GUI::GLGizmoEmboss::process(), Slic3r::GUI::GLGizmoSVG::process(), Slic3r::GUI::GLGizmoCut3D::process_contours(), Slic3r::GUI::GLGizmoCut3D::process_cut_line(), Slic3r::GUI::GLGizmoTextureDisplacement::queue_prepare(), Slic3r::GUI::GLGizmoTextureDisplacement::queue_preview_job(), Slic3r::GUI::GLGizmoBrimEars::register_single_mesh_pick(), Slic3r::GUI::GLGizmoMeasure::register_single_mesh_pick(), Slic3r::GUI::GLGizmoEmboss::reinit_text_lines(), Slic3r::GUI::GLGizmoMmuSegmentation::remap_filament_assignments(), Slic3r::GUI::GLGizmoTextureDisplacement::remove_texture_layer(), Slic3r::GUI::GLGizmoTextureDisplacement::render_adjust_texture_gizmo(), Slic3r::GUI::GLGizmoMove3D::render_align_preview(), Slic3r::GUI::GLGizmoAssembly::render_assembly_mode_combo(), render_combo(), Slic3r::GUI::GLGizmoCut3D::render_connectors_input_window(), Slic3r::GUI::GLGizmoPainterBase::render_cursor(), Slic3r::GUI::GLGizmoPainterBase::render_cursor_circle(), Slic3r::GUI::GLGizmoPainterBase::render_cursor_height_range(), Slic3r::GUI::GLGizmoCut3D::render_cut_mode_combo(), Slic3r::GUI::GLGizmoCut3D::render_cut_plane_input_window(), Slic3r::GUI::GLGizmoMeasure::render_dimensioning(), Slic3r::GUI::GLGizmoMeasure::render_follows_cursor(), Slic3r::GUI::GLGizmoPainterBase::render_follows_cursor(), Slic3r::GUI::GLGizmoTextureDisplacement::render_image_adjust_window(), render_input_window(), Slic3r::GUI::GLGizmoTextureDisplacement::render_island_overlay(), Slic3r::GUI::GLGizmoTextureDisplacement::render_paint_overlay(), Slic3r::GUI::GLGizmoTextureDisplacement::render_painted_colors(), Slic3r::GUI::GLGizmoFdmSupports::render_painter_gizmo(), Slic3r::GUI::GLGizmoFuzzySkin::render_painter_gizmo(), Slic3r::GUI::GLGizmoMmuSegmentation::render_painter_gizmo(), Slic3r::GUI::GLGizmoSeam::render_painter_gizmo(), Slic3r::GUI::GLGizmoTextureDisplacement::render_painter_gizmo(), Slic3r::GUI::GLGizmoTextureDisplacement::render_preview_mesh(), Slic3r::GUI::GLGizmoTextureDisplacement::render_seam_overlay(), Slic3r::GUI::GLGizmoTextureDisplacement::render_shaded_preview_mesh(), Slic3r::GUI::GLGizmoTextureDisplacement::render_subdivide_preview(), Slic3r::GUI::GLGizmoTextureDisplacement::render_texture_library_popup(), Slic3r::GUI::GLGizmoTextureDisplacement::render_tip_media(), Slic3r::GUI::GLGizmoCut3D::render_tooltip_button(), Slic3r::GUI::GLGizmoFdmSupports::render_tooltip_button(), Slic3r::GUI::GLGizmoFuzzySkin::render_tooltip_button(), Slic3r::GUI::GLGizmoMmuSegmentation::render_tooltip_button(), Slic3r::GUI::GLGizmoPainterBase::render_triangles(), Slic3r::GUI::GLGizmoTextureDisplacement::render_uvcheck_mesh(), Slic3r::GUI::GLGizmoTextureDisplacement::render_wireframe_overlay(), Slic3r::GUI::GLGizmoSimplify::request_rerender(), Slic3r::GUI::GLGizmoCut3D::reset_cut_plane(), Slic3r::GUI::GLGizmoEmboss::reset_to_default_style(), Slic3r::GUI::GLGizmoTextureDisplacement::run_stage_debug(), Slic3r::GUI::GLGizmoFdmSupports::run_thread(), Slic3r::GUI::GLGizmoTextureDisplacement::run_uv_command(), Slic3r::GUI::GLGizmoTextureDisplacement::seam_edge_at(), Slic3r::GUI::GLGizmoTextureDisplacement::seam_vertex_at(), Slic3r::GUI::GLGizmoFdmSupports::select_facets_by_angle(), Slic3r::GUI::GLGizmoTextureDisplacement::select_visible_faces(), Slic3r::GUI::GLGizmoTextureDisplacement::select_whole_model(), Slic3r::GUI::GLGizmoCut3D::set_connectors_editing(), Slic3r::GUI::GLGizmoMeasure::set_distance(), Slic3r::GUI::GLGizmoTextureDisplacement::set_layer_texture(), Slic3r::GUI::GLGizmoMeasure::set_parallel_distance(), Slic3r::GUI::GLGizmoMeasure::set_to_around_center_of_faces(), Slic3r::GUI::GLGizmoMeasure::set_to_parallel(), Slic3r::GUI::GLGizmoMeasure::set_to_reverse_rotation(), Slic3r::GUI::GLGizmoEmboss::set_volume_by_selection(), Slic3r::GUI::GLGizmoSVG::set_volume_by_selection(), Slic3r::GUI::GLGizmoCut3D::set_volumes_picking_state(), Slic3r::GUI::GLGizmoTextureDisplacement::show_debug_stage(), Slic3r::GUI::GLGizmoTextureDisplacement::show_projector(), Slic3r::GUI::GLGizmoTextureDisplacement::smooth_model(), Slic3r::GUI::GLGizmoTextureDisplacement::subdivide_model(), Slic3r::GUI::GLGizmoCut3D::toggle_model_objects_visibility(), Slic3r::GUI::GLGizmoCut3D::transformed_bounding_box(), Slic3r::GUI::GLGizmoBrimEars::unproject_on_mesh(), Slic3r::GUI::GLGizmoCut3D::update_bb(), Slic3r::GUI::GLGizmoBrimEars::update_cache_radius(), Slic3r::GUI::GLGizmoCut3D::update_clipper(), Slic3r::GUI::GLGizmoPainterBase::update_contours(), Slic3r::GUI::GLGizmoScale3D::update_grabbers_data(), Slic3r::GUI::GLGizmoTextureDisplacement::update_image_adjust_preview(), Slic3r::GUI::GLGizmoBrimEars::update_model_object(), Slic3r::GUI::GLGizmoFdmSupports::update_model_object(), Slic3r::GUI::GLGizmoFuzzySkin::update_model_object(), Slic3r::GUI::GLGizmoMmuSegmentation::update_model_object(), Slic3r::GUI::GLGizmoSeam::update_model_object(), Slic3r::GUI::GLGizmoTextureDisplacement::update_model_object(), Slic3r::GUI::GLGizmoPainterBase::update_raycast_cache(), Slic3r::GUI::GLGizmoBrimEars::update_raycasters(), use_grabbers(), Slic3r::GUI::GLGizmoEmboss::volume_transformation_changed(), Slic3r::GUI::GLGizmoSVG::volume_transformation_changed(), and Slic3r::GUI::GLGizmoEmboss::volume_transformation_changing().

◆ m_part_previews

std::vector<std::unique_ptr<PartPreview> > Slic3r::GUI::GLGizmoTextureDisplacement::m_part_previews

◆ m_picker_open_request

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_picker_open_request = false
private

◆ m_picker_reveal_selected

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_picker_reveal_selected = false
private

◆ m_picker_scroll_applied

float Slic3r::GUI::GLGizmoTextureDisplacement::m_picker_scroll_applied = -1.f
private

◆ m_picker_scroll_target

float Slic3r::GUI::GLGizmoTextureDisplacement::m_picker_scroll_target = 0.f
private

◆ m_picker_slot

int Slic3r::GUI::GLGizmoTextureDisplacement::m_picker_slot = -1
private

◆ m_prepare_in_progress

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_prepare_in_progress = false
private

◆ m_preview_color_runs

std::vector<PreviewColorRun> Slic3r::GUI::GLGizmoTextureDisplacement::m_preview_color_runs

◆ m_preview_generation

std::shared_ptr<std::atomic<uint64_t> > Slic3r::GUI::GLGizmoTextureDisplacement::m_preview_generation = std::make_shared<std::atomic<uint64_t>>(0)
private

◆ m_preview_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_preview_glmodel
private

◆ m_preview_its

indexed_triangle_set Slic3r::GUI::GLGizmoTextureDisplacement::m_preview_its
private

◆ m_preview_job_pending

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_preview_job_pending = false
private

◆ m_preview_job_running

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_preview_job_running = false
private

Referenced by queue_preview_job().

◆ m_preview_params_dirty

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_preview_params_dirty = false
private

◆ m_project_only_visible

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_project_only_visible = false
private

◆ m_projector_frame

TextureProjectorFrame* Slic3r::GUI::GLGizmoTextureDisplacement::m_projector_frame = nullptr
private

◆ m_projector_opacity

int Slic3r::GUI::GLGizmoTextureDisplacement::m_projector_opacity = 140
private

Referenced by show_projector().

◆ m_projector_tex_smoothing

float Slic3r::GUI::GLGizmoTextureDisplacement::m_projector_tex_smoothing = -1.f
private

◆ m_projector_tex_source

const void* Slic3r::GUI::GLGizmoTextureDisplacement::m_projector_tex_source = nullptr
private

◆ m_remesh_keep_sharp_edges

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_remesh_keep_sharp_edges = true
private

◆ m_remesh_sharp_angle_deg

float Slic3r::GUI::GLGizmoTextureDisplacement::m_remesh_sharp_angle_deg = 40.f
private

◆ m_remesh_target_edge_mm

float Slic3r::GUI::GLGizmoTextureDisplacement::m_remesh_target_edge_mm = 0.f
private

◆ m_rr

RaycastResult Slic3r::GUI::GLGizmoPainterBase::m_rr = {Vec2d::Zero(), -1, Vec3f::Zero(), 0}
mutableprivateinherited

◆ m_schedule_update

bool Slic3r::GUI::GLGizmoPainterBase::m_schedule_update = false
privateinherited

Referenced by data_changed(), and on_load().

◆ m_seam_anchor_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_seam_anchor_glmodel
private

◆ m_seam_edit_mode

◆ m_seam_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_seam_glmodel
private

◆ m_seam_hover_edge

std::pair<int, int> Slic3r::GUI::GLGizmoTextureDisplacement::m_seam_hover_edge { -1, -1 }
private

◆ m_seam_hover_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_seam_hover_glmodel
private

◆ m_seam_hover_vertex

int Slic3r::GUI::GLGizmoTextureDisplacement::m_seam_hover_vertex = -1
private

◆ m_seam_path_anchor

int Slic3r::GUI::GLGizmoTextureDisplacement::m_seam_path_anchor = -1
private

◆ m_seam_path_mode

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_seam_path_mode = false
private

◆ m_seed_fill_last_mesh_id

◆ m_selectors_stale

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_selectors_stale = false
private

◆ m_shaded_active_chart

int Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_active_chart = -1
private

◆ m_shaded_active_face

std::vector<uint8_t> Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_active_face
private

◆ m_shaded_baked_active_xf

Eigen::Matrix<float, 2, 3> Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_baked_active_xf = Eigen::Matrix<float, 2, 3>::Identity()
private

◆ m_shaded_island_delta

Eigen::Matrix<float, 2, 3> Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_island_delta = Eigen::Matrix<float, 2, 3>::Identity()
private

◆ m_shaded_patch_axis

Vec3f Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_patch_axis = Vec3f::UnitZ()
private

◆ m_shaded_patch_center

Vec3f Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_patch_center = Vec3f::Zero()
private

◆ m_shaded_preview_dirty

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_preview_dirty = false
private

◆ m_shaded_preview_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_preview_glmodel
private

◆ m_shaded_preview_palette

std::vector<PaletteEntry> Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_preview_palette
private

◆ m_shaded_preview_uses_vertex_uv

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_preview_uses_vertex_uv = false
private

◆ m_shaded_projection_mode

int Slic3r::GUI::GLGizmoTextureDisplacement::m_shaded_projection_mode = 0
private

◆ m_shortcut

◆ m_shortcuts_brush

std::vector<std::pair<wxString, wxString> > Slic3r::GUI::GLGizmoTextureDisplacement::m_shortcuts_brush
private

Referenced by on_init().

◆ m_shortcuts_bucket_fill

std::vector<std::pair<wxString, wxString> > Slic3r::GUI::GLGizmoTextureDisplacement::m_shortcuts_bucket_fill
private

Referenced by on_init().

◆ m_shortcuts_gap_fill

std::vector<std::pair<wxString, wxString> > Slic3r::GUI::GLGizmoTextureDisplacement::m_shortcuts_gap_fill
private

Referenced by on_init().

◆ m_shortcuts_sliders

std::vector<std::pair<wxString, wxString> > Slic3r::GUI::GLGizmoTextureDisplacement::m_shortcuts_sliders
private

Referenced by on_init().

◆ m_show_resolution

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_show_resolution = false
private

◆ m_show_uv_editor

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_show_uv_editor = false
private

◆ m_slider_type_request

std::string Slic3r::GUI::GLGizmoTextureDisplacement::m_slider_type_request
private

Referenced by on_render_input_window().

◆ m_slider_type_request_frame

int Slic3r::GUI::GLGizmoTextureDisplacement::m_slider_type_request_frame = 0
private

Referenced by on_render_input_window().

◆ m_smart_fill_angle

◆ m_sprite_id

unsigned int Slic3r::GUI::GLGizmoBase::m_sprite_id
protectedinherited

Referenced by GLGizmoBase(), and get_sprite_id().

◆ m_state

◆ m_subdivide_adaptive

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_adaptive = false
private

◆ m_subdivide_border_mm

float Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_border_mm = 0.4f
private

◆ m_subdivide_budget_k

int Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_budget_k = 750
private

◆ m_subdivide_color_mm

float Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_color_mm = 0.3f
private

◆ m_subdivide_count

int Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_count = 1
private

◆ m_subdivide_detail_mm

float Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_detail_mm = 0.05f
private

◆ m_subdivide_editing

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_editing = false
private

◆ m_subdivide_feature

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_feature = false
private

◆ m_subdivide_min_edge_mm

float Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_min_edge_mm = 0.1f
private

◆ m_subdivide_preview_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_preview_glmodel
private

◆ m_subdivide_preview_tris

int Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_preview_tris = -1
private

◆ m_subdivide_target_mm

float Slic3r::GUI::GLGizmoTextureDisplacement::m_subdivide_target_mm = 0.f
private

◆ m_thumbnail_smoothing

std::array<float, TEXTURE_DISPLACEMENT_MAX_LAYERS> Slic3r::GUI::GLGizmoTextureDisplacement::m_thumbnail_smoothing {}
private
1130{};

Referenced by get_layer_thumbnail(), and swap_layer_slots().

◆ m_thumbnail_source

std::array<std::weak_ptr<const std::vector<unsigned char> >, TEXTURE_DISPLACEMENT_MAX_LAYERS> Slic3r::GUI::GLGizmoTextureDisplacement::m_thumbnail_source
private

◆ m_thumbnails

std::array<std::unique_ptr<GLTexture>, TEXTURE_DISPLACEMENT_MAX_LAYERS> Slic3r::GUI::GLGizmoTextureDisplacement::m_thumbnails
private

◆ m_tip_hover_since

double Slic3r::GUI::GLGizmoTextureDisplacement::m_tip_hover_since = 0.
private

Referenced by panel_tooltip().

◆ m_tip_item

ImVec2 Slic3r::GUI::GLGizmoTextureDisplacement::m_tip_item {-1.f, -1.f}
private
1285{-1.f, -1.f};

Referenced by panel_tooltip().

◆ m_tip_last_shown

double Slic3r::GUI::GLGizmoTextureDisplacement::m_tip_last_shown = -1e9
private

Referenced by panel_tooltip().

◆ m_tip_media

AnimatedTextureCache Slic3r::GUI::GLGizmoTextureDisplacement::m_tip_media
private

Referenced by render_tip_media().

◆ m_tip_media_frame

int Slic3r::GUI::GLGizmoTextureDisplacement::m_tip_media_frame = -2
private

Referenced by render_tip_media().

◆ m_tip_media_key

std::string Slic3r::GUI::GLGizmoTextureDisplacement::m_tip_media_key
private

Referenced by render_tip_media().

◆ m_tip_media_start

double Slic3r::GUI::GLGizmoTextureDisplacement::m_tip_media_start = 0.
private

Referenced by render_tip_media().

◆ m_tip_settled

int Slic3r::GUI::GLGizmoTextureDisplacement::m_tip_settled = 0
private

Referenced by panel_tooltip().

◆ m_tip_shown_frame

int Slic3r::GUI::GLGizmoTextureDisplacement::m_tip_shown_frame = -2
private

Referenced by panel_tooltip().

◆ m_tool_type

◆ m_tooltip_anchor_max

ImVec2 Slic3r::GUI::GLGizmoTextureDisplacement::m_tooltip_anchor_max {0.f, 0.f}
private

◆ m_tooltip_anchor_min

ImVec2 Slic3r::GUI::GLGizmoTextureDisplacement::m_tooltip_anchor_min {0.f, 0.f}
private

◆ m_triangle_selectors

std::vector<std::unique_ptr<TriangleSelectorGUI> > Slic3r::GUI::GLGizmoPainterBase::m_triangle_selectors
protectedinherited

Referenced by gizmo_event(), Slic3r::GUI::GLGizmoMmuSegmentation::init_model_triangle_selectors(), Slic3r::GUI::GLGizmoFdmSupports::on_render_input_window(), Slic3r::GUI::GLGizmoFuzzySkin::on_render_input_window(), Slic3r::GUI::GLGizmoMmuSegmentation::on_render_input_window(), Slic3r::GUI::GLGizmoSeam::on_render_input_window(), Slic3r::GUI::GLGizmoTextureDisplacement::on_render_input_window(), on_set_state(), Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_paint_overlay(), Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_shaded_preview_mesh(), Slic3r::GUI::GLGizmoTextureDisplacement::rebuild_uvcheck_mesh(), Slic3r::GUI::GLGizmoMmuSegmentation::remap_filament_assignments(), Slic3r::GUI::GLGizmoTextureDisplacement::render_painter_gizmo(), render_triangles(), Slic3r::GUI::GLGizmoFdmSupports::select_facets_by_angle(), Slic3r::GUI::GLGizmoTextureDisplacement::select_visible_faces(), Slic3r::GUI::GLGizmoTextureDisplacement::select_whole_model(), Slic3r::GUI::GLGizmoFdmSupports::tool_changed(), Slic3r::GUI::GLGizmoMmuSegmentation::tool_changed(), Slic3r::GUI::GLGizmoSeam::tool_changed(), Slic3r::GUI::GLGizmoFdmSupports::update_from_model_object(), Slic3r::GUI::GLGizmoFuzzySkin::update_from_model_object(), Slic3r::GUI::GLGizmoSeam::update_from_model_object(), Slic3r::GUI::GLGizmoTextureDisplacement::update_from_model_object(), Slic3r::GUI::GLGizmoFdmSupports::update_model_object(), Slic3r::GUI::GLGizmoFuzzySkin::update_model_object(), Slic3r::GUI::GLGizmoMmuSegmentation::update_model_object(), Slic3r::GUI::GLGizmoSeam::update_model_object(), Slic3r::GUI::GLGizmoTextureDisplacement::update_model_object(), Slic3r::GUI::GLGizmoMmuSegmentation::update_triangle_selectors_colors(), and Slic3r::GUI::GLGizmoMmuSegmentation::update_used_filaments().

◆ m_triangle_splitting_enabled

bool Slic3r::GUI::GLGizmoPainterBase::m_triangle_splitting_enabled = true
protectedinherited

Referenced by gizmo_event().

◆ m_undocked

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_undocked = false
private

◆ m_use_shaded_preview

◆ m_uv_apply_connected_net

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_apply_connected_net = false
private

Referenced by run_uv_command(), and update_uv_editor().

◆ m_uv_check_mode

◆ m_uv_command_queue

std::vector<std::pair<int, float> > Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_command_queue
private

◆ m_uv_editor_bbox_max

Vec2f Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_editor_bbox_max = Vec2f::Zero()
private

◆ m_uv_editor_bbox_min

Vec2f Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_editor_bbox_min = Vec2f::Zero()
private

◆ m_uv_editor_bg

UVBackground Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_editor_bg = UVBackground::None
private

Referenced by run_uv_command(), and update_uv_editor().

◆ m_uv_editor_bg_adjust

TextureAdjustments Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_editor_bg_adjust
private

Referenced by update_uv_editor().

◆ m_uv_editor_bg_image

const void* Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_editor_bg_image = nullptr
private

Referenced by update_uv_editor().

◆ m_uv_editor_bg_smoothing

float Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_editor_bg_smoothing = -1.f
private

Referenced by update_uv_editor().

◆ m_uv_editor_distortion_colors

std::vector<ColorRGBA> Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_editor_distortion_colors
private

◆ m_uv_editor_state

UVEditorState Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_editor_state
private

◆ m_uv_editor_unwrap

◆ m_uv_overrides_sig

size_t Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_overrides_sig = 0
private

Referenced by update_uv_editor().

◆ m_uv_select_mode

int Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_select_mode = 0
private

Referenced by run_uv_command(), and update_uv_editor().

◆ m_uv_thumb_rgb

std::vector<unsigned char> Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_thumb_rgb
private

Referenced by push_uv_pane_state().

◆ m_uv_thumb_source

const void* Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_thumb_source = nullptr
private

Referenced by push_uv_pane_state().

◆ m_uv_unwrap_pending

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_uv_unwrap_pending = false
private

Referenced by run_uv_command(), and update_uv_editor().

◆ m_uvcheck_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_uvcheck_glmodel
private

◆ m_uvcheck_patch_axis

Vec3f Slic3r::GUI::GLGizmoTextureDisplacement::m_uvcheck_patch_axis = Vec3f::UnitZ()
private

◆ m_uvcheck_patch_center

Vec3f Slic3r::GUI::GLGizmoTextureDisplacement::m_uvcheck_patch_center = Vec3f::Zero()
private

◆ m_uvcheck_projection_mode

int Slic3r::GUI::GLGizmoTextureDisplacement::m_uvcheck_projection_mode = 0
private

◆ m_uvcheck_uses_vertex_uv

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_uvcheck_uses_vertex_uv = false
private

◆ m_v2_rec

V2Resolution Slic3r::GUI::GLGizmoTextureDisplacement::m_v2_rec
private

Referenced by v2_recommendation().

◆ m_v2_rec_key

std::string Slic3r::GUI::GLGizmoTextureDisplacement::m_v2_rec_key
private

Referenced by v2_recommendation().

◆ m_vertical_only

◆ m_volumes_index

int Slic3r::GUI::GLGizmoPainterBase::m_volumes_index = 0
mutableprivateinherited

◆ m_wireframe_overlay

bool Slic3r::GUI::GLGizmoTextureDisplacement::m_wireframe_overlay = false
private

◆ m_wireframe_overlay_glmodel

GLModel Slic3r::GUI::GLGizmoTextureDisplacement::m_wireframe_overlay_glmodel
private

◆ m_wireframe_overlay_vcount

size_t Slic3r::GUI::GLGizmoTextureDisplacement::m_wireframe_overlay_vcount = 0
private

◆ s_sphere

std::shared_ptr< GLModel > Slic3r::GUI::GLGizmoPainterBase::s_sphere = nullptr
staticprivateinherited

◆ SmartFillAngleMax

◆ SmartFillAngleMin

◆ SmartFillAngleStep

float Slic3r::GUI::GLGizmoPainterBase::SmartFillAngleStep = 1.f
staticconstexprprotectedinherited

Referenced by gizmo_event().

◆ UNDOCKED_CONFIG_KEY

const char* Slic3r::GUI::GLGizmoTextureDisplacement::UNDOCKED_CONFIG_KEY = "texture_displacement_panel_undocked"
staticconstexprprivate

◆ UV_THUMB_PX

int Slic3r::GUI::GLGizmoTextureDisplacement::UV_THUMB_PX = 64
staticconstexprprivate

Referenced by push_uv_pane_state().


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