Files
hcie-rust-v3.05/hcie-iced-app/crates/hcie-iced-gui/src/app/transform_dirty.rs
T

87 lines
3.3 KiB
Rust
Raw Normal View History

//! Selection-transform preview invalidation utilities.
//!
//! **Purpose:** Computes canvas-clipped dirty bounds for floating selection
//! previews so both their previous and current positions can be uploaded to the
//! persistent GPU texture during interactive movement.
//!
//! **Logic & Workflow:** Preview geometry is rounded exactly like the CPU
//! compositor, clamped to the canvas origin, and clipped at the right and
//! bottom canvas edges. Empty or fully out-of-canvas rectangles return `None`.
//!
//! **Side Effects / Dependencies:** The module is deterministic and has no I/O,
//! global state, allocation, engine dependency, or GPU dependency.
/// Calculates the clipped pixel bounds occupied by a transform preview.
///
/// **Purpose:** Produces the dirty rectangle that must be restored or redrawn
/// when a floating selection changes position or dimensions.
///
/// **Logic & Workflow:** Delegates to the shared rotated-corner AABB calculation
/// used by both preview and apply, clips it to the canvas, and rejects empty rectangles.
///
/// **Arguments:**
/// - `transform`: Floating selection source and affine geometry.
/// - `canvas_width`, `canvas_height`: Clipping dimensions of the document.
///
/// **Returns:** `Some([x0, y0, x1, y1])` for a non-empty clipped rectangle, or
/// `None` when the preview cannot affect the canvas.
///
/// **Side Effects / Dependencies:** None.
pub(crate) fn preview_bounds(
transform: &crate::selection::SelectionTransform,
canvas_width: u32,
canvas_height: u32,
) -> Option<[u32; 4]> {
crate::selection::transform::transformed_bounds(transform, canvas_width, canvas_height)
}
#[cfg(test)]
mod tests {
use crate::selection::SelectionTransform;
use iced::Vector;
use super::preview_bounds;
fn transform(x: f32, y: f32) -> SelectionTransform {
SelectionTransform {
pixels: vec![255; 30 * 40 * 4],
width: 30,
height: 40,
pos: Vector::new(x, y),
size: Vector::new(30.0, 40.0),
rotation: 0.0,
}
}
/// Verifies that consecutive preview positions expose both the old and new
/// rectangles needed by the caller's dirty-region union.
///
/// **Purpose:** Guards against stale GPU pixels remaining at an old drag
/// position when only the current preview rectangle is uploaded.
///
/// **Logic & Workflow:** Computes two partially overlapping positions and
/// confirms their bounds preserve the complete restoration/redraw extent.
///
/// **Arguments & Returns:** Takes no arguments and returns nothing;
/// assertion failures identify incorrect transform bounds.
///
/// **Side Effects / Dependencies:** None.
#[test]
fn consecutive_positions_retain_old_and_new_extents() {
let previous = preview_bounds(&transform(10.0, 20.0), 100, 100).unwrap();
let current = preview_bounds(&transform(25.0, 30.0), 100, 100).unwrap();
assert_eq!(previous, [10, 20, 40, 60]);
assert_eq!(current, [25, 30, 55, 70]);
assert_eq!(
[
previous[0].min(current[0]),
previous[1].min(current[1]),
previous[2].max(current[2]),
previous[3].max(current[3]),
],
[10, 20, 55, 70],
);
}
}