wolfhece._overlays ================== .. py:module:: wolfhece._overlays .. autoapi-nested-parse:: Overlay and panel classes extracted from PyDraw.py. These classes provide on-canvas HUD overlays and floating control panels for the WolfMapViewer. Author: HECE - University of Liege, Pierre Archambeau Date: 2024 Copyright (c) 2024 University of Liege. All rights reserved. This script and its content are protected by copyright law. Unauthorized copying or distribution of this file, via any medium, is strictly prohibited. Module Contents --------------- .. py:class:: HillshadePanel(mapviewer: wolfhece.PyDraw.WolfMapViewer) Bases: :py:obj:`wx.Frame` .. autoapi-inheritance-diagram:: wolfhece._overlays.HillshadePanel :parts: 1 :private-bases: Persistent panel for hillshade / sun-lighting controls. Provides live sliders for altitude, azimuth, intensity, checkboxes for enable and multi-directional mode, and a sunrise animation button. .. py:attribute:: _ANIM_INTERVAL_MS :value: 33 .. py:attribute:: _ANIM_SPEED_DEG_S :value: 30.0 .. py:attribute:: mapviewer .. py:attribute:: _panel .. py:attribute:: _chk_enable .. py:attribute:: _alt .. py:attribute:: _az .. py:attribute:: _int .. py:attribute:: _chk_multi .. py:attribute:: _chk_sync .. py:attribute:: _z_exag .. py:attribute:: _spec .. py:attribute:: _gloss .. py:attribute:: _hl .. py:attribute:: _anim_speed .. py:attribute:: _btn_apply .. py:attribute:: _btn_anim .. py:attribute:: _btn_natural .. py:attribute:: _btn_pseudo .. py:attribute:: _btn_save .. py:attribute:: _timer .. py:method:: _on_change(event) Called on Enter key, Apply button, or checkbox — push to mapviewer. .. py:method:: _on_toggle_anim(event) .. py:method:: _apply_preset(altitude, azimuth, intensity, z_exag, multidirectional, specular=0.0, glossiness=0.5, highlight=1.0) Apply a preset and sync all controls. .. py:method:: _on_preset_natural(event) Standard cartographic hillshade: NW light, moderate settings. .. py:method:: _on_preset_pseudoscopic(event) Pseudoscopic illusion: SE light + strong Z exaggeration. .. py:method:: _on_timer(event) .. py:method:: _on_close(event) .. py:method:: _on_sync_toggle(event) Toggle synchronised / per-array mode. .. py:method:: _on_save(event) Persist material params to sidecar files. .. py:method:: refresh_from_params() Refresh spin controls from the active material params source. .. py:class:: HillshadeOverlay(mapviewer: wolfhece.PyDraw.WolfMapViewer) On-canvas HUD for interactive hillshade control. Draws an azimuth ring and altitude bar in the bottom-right corner of the GL canvas. Responds to mouse drag on these widgets. Only active while the :class:`HillshadePanel` is open. .. py:attribute:: _MARGIN :value: 20 .. py:attribute:: _RING_RADIUS :value: 45 .. py:attribute:: _BAR_WIDTH :value: 14 .. py:attribute:: _MINI_BAR_WIDTH :value: 10 .. py:attribute:: _GAP :value: 18 .. py:attribute:: _MINI_GAP :value: 8 .. py:attribute:: _BG_RGBA :value: (0.1, 0.1, 0.1, 0.5) .. py:attribute:: _OUTLINE_RGBA :value: (0.8, 0.8, 0.8, 0.8) .. py:attribute:: _SUN_RGBA :value: (1.0, 0.9, 0.2, 0.9) .. py:attribute:: _SUN_FILL_RGBA :value: (1.0, 0.9, 0.2, 0.4) .. py:attribute:: _SPEC_RGBA :value: (0.9, 0.9, 1.0, 0.9) .. py:attribute:: _SPEC_FILL :value: (0.7, 0.7, 1.0, 0.4) .. py:attribute:: _ROUGH_RGBA :value: (0.6, 0.85, 0.6, 0.9) .. py:attribute:: _ROUGH_FILL :value: (0.4, 0.7, 0.4, 0.4) .. py:attribute:: _METAL_RGBA :value: (0.9, 0.7, 0.3, 0.9) .. py:attribute:: _METAL_FILL :value: (0.8, 0.6, 0.2, 0.4) .. py:attribute:: mapviewer .. py:attribute:: _dragging :type: str | None :value: None .. py:method:: _ring_center() .. py:method:: _bar_rect() Return (x0, y_bottom, x1, y_top) for the altitude bar. .. py:method:: _mini_bar_rect(index) Return (x0, y0, x1, y1) for mini bar *index* (0=S, 1=R, 2=M). Mini bars sit to the left of the altitude bar. .. py:method:: _wx_to_gl(wx_x, wx_y) .. py:method:: hit_test(wx_x, wx_y) Return ``'azimuth'``, ``'altitude'``, ``'specular'``, ``'glossiness'``, ``'highlight'``, or *None*. .. py:method:: on_drag(wx_x, wx_y) Update the hillshade parameter corresponding to ``_dragging``. .. py:method:: _sync_panel() Push current mapviewer values back into the HillshadePanel controls. .. py:method:: handle_wheel(rotation, delta, ctrl, shift) Handle mouse wheel when hillshade panel is open. Returns *True* if the event was consumed. .. py:method:: draw() Draw azimuth ring + altitude bar as a pixel-space HUD. .. py:method:: _draw_azimuth_ring(math, glColor4f, glLineWidth, glBegin, glEnd, glVertex2f, GL_TRIANGLE_FAN, GL_LINE_LOOP, GL_LINES) .. py:method:: _draw_altitude_bar(glColor4f, glLineWidth, glBegin, glEnd, glVertex2f, GL_LINE_LOOP, GL_LINES, GL_QUADS) .. py:method:: _draw_mini_bars(glColor4f, glLineWidth, glBegin, glEnd, glVertex2f, GL_LINE_LOOP, GL_LINES, GL_QUADS) Draw the three S / R / M mini-bars. .. py:class:: _ToolButton(icon: str, label: str, *, action: str | None = None, is_toggle: bool = False, callback=None) Descriptor for one toolbar button. .. py:attribute:: icon .. py:attribute:: label .. py:attribute:: action :value: None .. py:attribute:: is_toggle :value: False .. py:attribute:: callback :value: None .. py:attribute:: SEPARATOR :value: None .. py:class:: CutFillOverlay(mapviewer: wolfhece.PyDraw.WolfMapViewer) Two side-by-side pie-chart HUDs showing cut/fill earthwork volumes. The **Zoom** pie covers only the cells currently visible in the viewport; the **Global** pie covers the entire active array. Both measure the signed difference ``z_ref − z_current``: * **Cut** (orange): cells where the terrain was lowered (ref > current). * **Fill** (teal): cells where the terrain was raised (ref < current). The overlay is visible only when the sculpt or profile brush is active *and* a reference snapshot has been frozen. It is placed at the bottom-right corner, automatically pushed above the hillshade overlay when that one is also on screen. .. py:attribute:: _GLOBAL_THROTTLE_S :type: float :value: 0.4 .. py:attribute:: PIE_R :value: 65 .. py:attribute:: CARD_PAD :value: 10 .. py:attribute:: TITLE_H :value: 15 .. py:attribute:: VALUE_H :value: 36 .. py:attribute:: GAP :value: 14 .. py:attribute:: MARGIN_X :value: 16 .. py:attribute:: MARGIN_Y :value: 16 .. py:attribute:: HS_MARGIN :value: 12 .. py:property:: CARD_W .. py:property:: CARD_H .. py:attribute:: BG_RGBA :value: (0.06, 0.06, 0.06, 0.62) .. py:attribute:: OUTLINE_RGBA :value: (0.7, 0.7, 0.7, 0.65) .. py:attribute:: CUT_RGBA :value: (1.0, 0.52, 0.12, 0.88) .. py:attribute:: CUT_FILL :value: (1.0, 0.52, 0.12, 0.38) .. py:attribute:: FILL_RGBA :value: (0.18, 0.8, 0.88, 0.88) .. py:attribute:: FILL_FILL :value: (0.18, 0.8, 0.88, 0.38) .. py:attribute:: ZERO_RGBA :value: (0.35, 0.35, 0.35, 0.55) .. py:attribute:: TITLE_RGBA :value: (0.88, 0.88, 0.88, 1.0) .. py:attribute:: CUT_TEXT :value: (1.0, 0.65, 0.3, 1.0) .. py:attribute:: FILL_TEXT :value: (0.3, 0.9, 0.95, 1.0) .. py:attribute:: enabled :type: bool :value: True .. py:attribute:: mapviewer .. py:attribute:: _g_cut :type: float :value: 0.0 .. py:attribute:: _g_fill :type: float :value: 0.0 .. py:attribute:: _z_cut :type: float :value: 0.0 .. py:attribute:: _z_fill :type: float :value: 0.0 .. py:attribute:: _dirty :type: bool :value: True .. py:attribute:: _zoom_dirty :type: bool :value: True .. py:attribute:: _last_viewport :type: tuple :value: () .. py:attribute:: _last_global_t :type: float :value: -1000000000.0 .. py:attribute:: _last_zoom_t :type: float :value: -1000000000.0 .. py:method:: invalidate() -> None Mark both caches dirty. Call after each brush stroke. .. py:method:: force_recompute() -> None Bypass throttle and recompute global immediately. Call when brushing ends so the final tally is always accurate. .. py:method:: _get_brush_info() Return ``(ref_array, live_data, dx, dy, nullvalue)`` or *None*. Pulls from whichever brush (sculpt or profile) is currently active. .. py:method:: _recompute_global() -> None .. py:method:: _compute_zoom() Return ``(cut_m3, fill_m3)`` restricted to the current viewport. .. py:method:: _vol_str(v: float) -> str :staticmethod: .. py:method:: _card_bottom_y() -> float Bottom-left y (GL pixels) for the cards, avoiding the hillshade. .. py:method:: _centers() Return ``(cx1, cx2, cy)`` — disc centres for Zoom / Global pies. .. py:method:: _draw_disc_sector(math, glColor4f, glBegin, glEnd, glVertex2f, GL_TRIANGLE_FAN, cx, cy, r, a_start, a_end, color, n=72) :staticmethod: Draw a filled pie sector from *a_start* to *a_end* (CCW radians). .. py:method:: draw() -> None Draw both cut/fill pie chart cards in GL pixel space. Called every repaint from ``WolfMapViewer.Paint()``. .. py:method:: _draw_card(cx, cy, cut, fill, title, math, glColor4f, glLineWidth, glBegin, glEnd, glVertex2f, GL_TRIANGLE_FAN, GL_LINE_LOOP, GL_LINES) Draw the background rect and pie disc for one card. .. py:method:: _draw_text_card(cx, cy, cut, fill, title, tr, px_mvp, vp, math) Draw the text labels for one pie card using pixel-space MVP. .. py:class:: ToolbarOverlay(mapviewer: wolfhece.PyDraw.WolfMapViewer) On-canvas contextual toolbar that appears in the top-left corner. Shows a small indicator (three dots) when collapsed. Expands vertically on hover to show tool buttons relevant to the currently active object type (vectors, arrays, point clouds, cross-sections, tiles). Toggled completely on/off with the ``T`` key. .. py:attribute:: _INDICATOR_X :value: 12 .. py:attribute:: _INDICATOR_Y_TOP :value: 16 .. py:attribute:: _INDICATOR_DOT_R :value: 3 .. py:attribute:: _INDICATOR_DOT_GAP :value: 8 .. py:attribute:: _INDICATOR_HIT_W :value: 28 .. py:attribute:: _INDICATOR_HIT_H :value: 40 .. py:attribute:: _BTN_SIZE :value: 30 .. py:attribute:: _BTN_GAP :value: 3 .. py:attribute:: _BAR_PADDING :value: 6 .. py:attribute:: _BAR_LEFT :value: 8 .. py:attribute:: _COLLAPSE_DELAY_MS :value: 600 .. py:attribute:: _BG :value: (0.1, 0.1, 0.12, 0.75) .. py:attribute:: _BG_HOVER :value: (0.25, 0.25, 0.3, 0.85) .. py:attribute:: _BG_ACTIVE :value: (0.15, 0.45, 0.8, 0.9) .. py:attribute:: _BORDER :value: (0.55, 0.55, 0.6, 0.8) .. py:attribute:: _TEXT :value: (0.95, 0.95, 0.95, 1.0) .. py:attribute:: _TEXT_ACTIVE :value: (1.0, 1.0, 1.0, 1.0) .. py:attribute:: _SEP_COLOR :value: (0.45, 0.45, 0.5, 0.6) .. py:attribute:: _DOT_COLOR :value: (0.7, 0.7, 0.75, 0.8) .. py:attribute:: _TOOLTIP_BG :value: (0.08, 0.08, 0.1, 0.9) .. py:attribute:: _TOOLTIP_TEXT :value: (0.95, 0.93, 0.8, 1.0) .. py:attribute:: _BUTTONS_VECTOR :type: list .. py:attribute:: _BUTTONS_ARRAY :type: list .. py:attribute:: _BUTTONS_CLOUD :type: list .. py:attribute:: _BUTTONS_CS :type: list .. py:attribute:: _BUTTONS_TILE :type: list .. py:attribute:: mapviewer .. py:attribute:: _expanded :value: False .. py:attribute:: _hover_btn :type: int | None :value: None .. py:attribute:: _text_renderer :value: None .. py:attribute:: _collapse_timer :value: None .. py:attribute:: _last_context :type: str | None :value: None .. py:attribute:: _cached_buttons :type: list | None :value: None .. py:method:: _detect_context() -> str Return a context key based on the selected object in the tree. Multiple ``active_*`` attributes can be non-None simultaneously, so they cannot be used to decide which button set to show. The *selected_object* (tree selection) is the authoritative source. .. py:method:: _get_buttons() -> list | None Return the button list for the current context (cached). .. py:method:: _canvas_size() .. py:method:: _wx_to_gl(wx_x, wx_y, h) :staticmethod: .. py:method:: _indicator_rect_gl() GL rect (x0, y0, x1, y1) of the 3-dot indicator zone. .. py:method:: _bar_geometry() Return (x0, y_top, y_bot, bar_w, btn_rects) for expanded bar. *btn_rects* is a list of (bx0, by0, bx1, by1) in GL coords, one per non-separator button. .. py:method:: is_inside(wx_x, wx_y) -> bool True if the wx point is over the indicator or the expanded bar. .. py:method:: hit_test(wx_x, wx_y) -> int | None Return button index if wx point is over a button, else None. .. py:method:: on_mouse_move(wx_x, wx_y) -> bool Track hover. Returns True if the event should be consumed. .. py:method:: on_click(wx_x, wx_y) -> bool Handle a left click. Returns True if consumed. .. py:method:: _start_collapse_timer() .. py:method:: _stop_collapse_timer() .. py:method:: _on_collapse() .. py:method:: _tb_add_zone() .. py:method:: _tb_add_vector() .. py:method:: _tb_create_parallel() .. py:method:: _tb_select_all() .. py:method:: _tb_reset_selection() .. py:method:: _tb_dilate() .. py:method:: _tb_erode() .. py:method:: _tb_mask_inside() .. py:method:: _tb_mask_outside() .. py:method:: _tb_open_sculpt() .. py:method:: _tb_sculpt_mode(mode_value: str) -> None Open the sculpt panel, set brush mode, and activate sculpting. .. py:method:: _tb_sculpt_smooth() .. py:method:: _tb_sculpt_raise() .. py:method:: _tb_sculpt_lower() .. py:method:: _tb_sculpt_flatten() .. py:method:: _tb_sculpt_noise() .. py:method:: _tb_open_profile() Open (or raise) the floating ProfilePanel. .. py:method:: draw() Draw the toolbar overlay. Called from WolfMapViewer.Paint(). .. py:method:: _draw_indicator(w, h, glColor4f, glPointSize, glBegin, glEnd, glVertex2f, GL_POINTS) Draw three small dots in the top-left corner. .. py:method:: _draw_expanded(w, h, glBegin, glEnd, glVertex2f, glColor4f, glLineWidth, glPointSize, GL_QUADS, GL_LINES, GL_POINTS) Draw the full expanded toolbar. .. py:method:: _draw_button_icon(btn, bx0, by0, bx1, by1, is_active, w, h) Draw the icon from the texture atlas, centred in the button. .. py:method:: _draw_separators(buttons, btn_rects, x0, bar_w, glColor4f, glLineWidth, glBegin, glEnd, glVertex2f, GL_LINES) Draw horizontal separator lines between button groups. .. py:method:: _draw_tooltip(text, btn_rect, w, h, glColor4f, glBegin, glEnd, glVertex2f, GL_QUADS) Draw a tooltip to the right of a button. .. py:class:: PaletteOverlay(mapviewer: wolfhece.PyDraw.WolfMapViewer) On-canvas HUD for interactive colour-palette editing. Draws a horizontal gradient bar at the top of the GL canvas showing the active array's palette. Triangular cursors mark the *values* at each colour stop and can be dragged to reshape the palette. Only active while toggled on (key ``K``). .. py:attribute:: _MARGIN :value: 20 .. py:attribute:: _BAR_HEIGHT :value: 24 .. py:attribute:: _CURSOR_H :value: 12 .. py:attribute:: _LABEL_FONT_SIZE :value: 11 .. py:attribute:: _LABEL_GAP :value: 4 .. py:attribute:: _MIN_CURSOR_SEP :value: 3 .. py:attribute:: _HANDLE_W :value: 10 .. py:attribute:: _BG_RGBA :value: (0.12, 0.12, 0.12, 0.55) .. py:attribute:: _OUTLINE_RGBA :value: (0.8, 0.8, 0.8, 0.8) .. py:attribute:: _CURSOR_RGBA :value: (1.0, 1.0, 1.0, 0.9) .. py:attribute:: _CURSOR_ACTIVE_RGBA :value: (1.0, 0.85, 0.0, 1.0) .. py:attribute:: _LABEL_COLOR :value: (0.95, 0.93, 0.8, 1.0) .. py:attribute:: _LABEL_BG :value: (0.08, 0.08, 0.1, 0.9) .. py:attribute:: mapviewer .. py:attribute:: _dragging :type: int | None :value: None .. py:attribute:: _hover :type: int | None :value: None .. py:attribute:: _hover_gl_x :type: float | None :value: None .. py:attribute:: _text_renderer :value: None .. py:method:: _get_palette_owner() Return the selected object that owns a palette, or *None*. Returns a WolfArray or Wolfresults_2D picked from ``mapviewer.selected_object``. .. py:method:: _get_palette() Return the active array's or active 2D result's palette, or *None*. .. py:method:: _iter_underlying_arrays() Yield every WolfArray that backs the current palette owner. For a plain WolfArray owner, yields just that single array. For a WolfArrayMB owner, yields the parent *and* every sub-block. For a Wolfresults_2D owner, yields the ``_current`` WolfArray of every block via ``iter_current_arrays()``. .. py:method:: _bar_rect() Return (x0, y_bottom, x1, y_top) for the gradient bar. .. py:method:: _value_to_x(val, pal, x0, x1) Map a palette value to a pixel x-coordinate. .. py:method:: _x_to_value(px_x, pal, x0, x1) Map a pixel x-coordinate back to a palette value. .. py:method:: _wx_to_gl(wx_x, wx_y) .. py:method:: is_inside(wx_x, wx_y) Return *True* if the wx position is inside the overlay zone. .. py:method:: on_mouse_move(wx_x, wx_y) Track hover position. Returns *True* if mouse is over the bar. .. py:method:: hit_test(wx_x, wx_y) Return cursor index, ``-1`` (min handle), ``-2`` (max handle), or *None*. .. py:method:: on_drag(wx_x, wx_y) Update palette values based on drag position. .. py:method:: draw() Draw the palette bar + cursors + labels as a pixel-space HUD. .. py:method:: _draw_gradient_bar(pal, x0, y0, x1, y1, glColor4f, glColor3f, glBegin, glEnd, glVertex2f, GL_QUADS) Draw the colour gradient (or piecewise-constant blocks). .. py:method:: _draw_cursors(pal, x0, y0, x1, y1, glColor4f, glBegin, glEnd, glVertex2f, GL_TRIANGLES) Draw triangular cursors below the bar for each colour stop. .. py:method:: _draw_labels(pal, x0, y0, x1, y1, w, h) Draw min/max value labels with tooltip-style background. .. py:method:: _draw_hover_value(pal, x0, y0, x1, y1, w, h, glColor4f, glLineWidth, glBegin, glEnd, glVertex2f, GL_LINES) Draw a vertical hair-line + tooltip-style value label at hover. .. py:method:: on_double_click(wx_x, wx_y) Open a dialog to type an exact value for the clicked cursor. .. py:method:: on_right_click(wx_x, wx_y) Show context menu if click is inside the overlay area. .. py:attribute:: _PRESETS :type: dict .. py:method:: _apply_preset(key: str) Apply a domain-specific preset palette to the active array. .. py:method:: _apply_model_file(filename: str) Load a .pal file from wolfhece/models/ into the active palette. .. py:method:: _apply_palette_change() Rebuild palette internals + invalidate GPU cache + refresh.