wolfhece.wolf_array._clipping ============================= .. py:module:: wolfhece.wolf_array._clipping .. autoapi-nested-parse:: Clipping zones for WolfArray OpenGL rendering. Provides :class:`WolfArrayClipZone` (extends :class:`header_wolf`) to restrict rendering to predefined or dynamically-defined world-coordinate regions using the OpenGL scissor test. A typical use case is a "curtain" comparison: two overlapping arrays are stacked, and a draggable :class:`ClipSlider` lets the user reveal one array vs. the other by sliding a horizontal or vertical bar. Author: HECE - University of Liege, Pierre Archambeau Date: 2024 Copyright (c) 2024 University of Liege. All rights reserved. Module Contents --------------- .. py:data:: DEFAULT_COLOR :value: (1.0, 0.0, 1.0, 0.8) .. py:class:: ClipSliderEdge(*args, **kwds) Bases: :py:obj:`enum.Enum` .. autoapi-inheritance-diagram:: wolfhece.wolf_array._clipping.ClipSliderEdge :parts: 1 :private-bases: Which edge of the clip zone the slider controls. .. py:attribute:: LEFT :value: 'left' .. py:attribute:: RIGHT :value: 'right' .. py:attribute:: BOTTOM :value: 'bottom' .. py:attribute:: TOP :value: 'top' .. py:class:: WolfArrayClipZone(xmin: float = 0.0, ymin: float = 0.0, xmax: float = 1.0, ymax: float = 1.0, active: bool = True, owner_name: str = '', invert: bool = False) Bases: :py:obj:`wolfhece.wolf_array._header_wolf.header_wolf` .. autoapi-inheritance-diagram:: wolfhece.wolf_array._clipping.WolfArrayClipZone :parts: 1 :private-bases: Rectangular clipping zone in world coordinates. Extends :class:`header_wolf` so that existing spatial tools (:meth:`find_intersection`, :meth:`get_bounds`, …) work seamlessly. The zone is defined by world-coordinate bounds ``[xmin, xmax] × [ymin, ymax]``. When *active*, the zone is applied as an OpenGL scissor rectangle during rendering: only the pixels that fall inside the zone are drawn. Usage:: clip = WolfArrayClipZone(xmin=160000, ymin=130000, xmax=170000, ymax=140000) my_array.add_clip_zone(clip) Or from an existing header (another WolfArray for instance):: clip = WolfArrayClipZone.from_header(other_array) .. py:attribute:: active :value: True .. py:attribute:: owner_name :value: '' .. py:attribute:: invert :value: False .. py:attribute:: _sliders :type: list[ClipSlider] :value: [] .. py:method:: set_bounds_clip(xmin: float, ymin: float, xmax: float, ymax: float) Set the clipping zone from world-coordinate bounds. .. py:property:: clip_xmin :type: float .. py:property:: clip_xmax :type: float .. py:property:: clip_ymin :type: float .. py:property:: clip_ymax :type: float .. py:method:: world_to_scissor(view_xmin: float, view_ymin: float, view_xmax: float, view_ymax: float, vp_width: int, vp_height: int) -> tuple[int, int, int, int] Convert world-coordinate bounds to a ``glScissor`` rectangle. The returned ``(sx, sy, sw, sh)`` tuple is in window-pixel coordinates, clamped to the current viewport. :param view_xmin: Current viewport left (world coords). :param view_ymin: Current viewport bottom (world coords). :param view_xmax: Current viewport right (world coords). :param view_ymax: Current viewport top (world coords). :param vp_width: Viewport width in pixels. :param vp_height: Viewport height in pixels. .. py:method:: enable_scissor(view_xmin: float, view_ymin: float, view_xmax: float, view_ymax: float, vp_width: int, vp_height: int) Enable ``GL_SCISSOR_TEST`` restricted to this clip zone. .. py:method:: disable_scissor() :staticmethod: Disable ``GL_SCISSOR_TEST``. .. py:method:: draw_border(color: tuple = DEFAULT_COLOR, width: float = 2.0) Draw the clip-zone border as a world-coordinate rectangle. .. py:property:: sliders :type: list[ClipSlider] All :class:`ClipSlider` objects attached to this zone. .. py:property:: slider :type: Optional[ClipSlider] First attached slider, or ``None``. .. py:method:: attach_slider(edge: ClipSliderEdge = ClipSliderEdge.RIGHT, **kwargs) -> ClipSlider Create and attach a :class:`ClipSlider` controlling *edge*. Extra *kwargs* are forwarded to the :class:`ClipSlider` constructor. :return: The newly created slider. .. py:method:: serialize() -> dict Return a JSON-compatible dict describing this clip zone. .. py:method:: deserialize(data: dict) -> WolfArrayClipZone :classmethod: Reconstruct a clip zone from a serialized dict. .. py:method:: from_header(h: wolfhece.wolf_array._header_wolf.header_wolf, active: bool = True, owner_name: str = '') -> WolfArrayClipZone :classmethod: Create a clip zone matching a :class:`header_wolf` extent. .. py:method:: from_extent(xmin: float, ymin: float, xmax: float, ymax: float, active: bool = True, owner_name: str = '') -> WolfArrayClipZone :classmethod: Alias for the regular constructor (explicit factory). .. py:class:: ClipSlider(clip_zone: WolfArrayClipZone, edge: ClipSliderEdge = ClipSliderEdge.RIGHT, color: tuple = DEFAULT_COLOR, line_width: float = 3.0, grab_tolerance_px: int = 8, owner_name: str = '') Draggable horizontal or vertical bar controlling a clip-zone edge. Provides the visual bar, hit testing, and drag logic. Mouse-event dispatch must be wired from the host viewer (e.g. :class:`WolfMapViewer`). .. py:attribute:: clip_zone .. py:attribute:: edge .. py:attribute:: color :value: (1.0, 0.0, 1.0, 0.8) .. py:attribute:: line_width :value: 3.0 .. py:attribute:: grab_tolerance_px :value: 8 .. py:attribute:: owner_name :value: '' .. py:attribute:: _dragging :value: False .. py:property:: position :type: float Current world-coordinate position of the controlled edge. .. py:property:: is_horizontal :type: bool ``True`` if the bar is horizontal (top / bottom edge). .. py:property:: label :type: str Human-readable label for status-bar display. .. py:method:: draw(view_xmin: float, view_ymin: float, view_xmax: float, view_ymax: float) Draw the slider bar across the full viewport extent. Must be called **outside** any active scissor test so the bar is always fully visible. .. py:method:: hit_test(world_x: float, world_y: float, sx: float, sy: float) -> bool Check whether a world-coordinate point is close enough to grab. :param world_x: Pointer X in world coordinates. :param world_y: Pointer Y in world coordinates. :param sx: Pixels per world-unit (horizontal scale). :param sy: Pixels per world-unit (vertical scale). :return: ``True`` if within :attr:`grab_tolerance_px` pixels. .. py:method:: start_drag() Begin a drag operation. .. py:method:: update_drag(world_x: float, world_y: float) Update the controlled edge position during a drag. .. py:method:: end_drag() Finish a drag operation. .. py:property:: is_dragging :type: bool ``True`` while a drag is in progress. .. py:method:: serialize() -> dict Return a JSON-compatible dict. .. py:class:: ClipConfig(name: str, owner_name: str, zones: list[WolfArrayClipZone]) Named snapshot of clip zones for one array. .. py:attribute:: name .. py:attribute:: owner_name .. py:attribute:: zones .. py:method:: serialize() -> dict .. py:method:: deserialize(data: dict) -> ClipConfig :classmethod: .. py:class:: ClipConfigs Collection of named :class:`ClipConfig` snapshots. Follows the same pattern as :class:`Memory_Views`. .. py:attribute:: configs :type: dict[str, ClipConfig] .. py:method:: add(cfg: ClipConfig) .. py:method:: remove(name: str) .. py:method:: reset() .. py:method:: save(filename: str) .. py:method:: load(filename: str)