87 lines
3.3 KiB
Rust
87 lines
3.3 KiB
Rust
|
|
//! 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],
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|