///|
/// Return zero when a coordinate is negative.
fn max_zero(value : Int) -> Int {
if value < 0 {
0
} else {
value
}
}
///|
/// Return one when a dimension is empty or negative.
fn max_one(value : Int) -> Int {
if value < 1 {
1
} else {
value
}
}
///|
/// Return the smaller integer.
fn min_int(left : Int, right : Int) -> Int {
if left < right {
left
} else {
right
}
}
///|
/// Return the larger integer.
fn max_int(left : Int, right : Int) -> Int {
if left > right {
left
} else {
right
}
}
///|
/// Normalize a capture area into coordinates and dimensions accepted by native
/// screenshot APIs.
///
/// Coordinates are clamped to `0`. Width and height are clamped to `1`, which
/// keeps downstream API calls from producing empty rectangles. This is
/// intentionally conservative: it preserves valid values exactly and only fixes
/// values that desktop APIs commonly reject.
///
/// # Example
///
/// ```mbt check
/// test {
/// let area = @screenshots.clamp_area({ x: -10, y: 5, width: 0, height: -3 })
/// inspect(area, content="{ x: 0, y: 5, width: 1, height: 1 }")
/// }
/// ```
pub fn clamp_area(area : CaptureArea) -> CaptureArea {
{
x: max_zero(area.x),
y: max_zero(area.y),
width: max_one(area.width),
height: max_one(area.height),
}
}
///|
/// Return the number of pixels covered by a normalized capture area.
///
/// The input is first passed through `clamp_area`, so empty or negative
/// dimensions still produce a useful one-pixel minimum.
///
/// # Example
///
/// ```mbt check
/// test {
/// inspect(
/// @screenshots.pixel_count({ x: 0, y: 0, width: 4, height: 3 }),
/// content="12",
/// )
/// inspect(
/// @screenshots.pixel_count({ x: 0, y: 0, width: 0, height: 3 }),
/// content="3",
/// )
/// }
/// ```
pub fn pixel_count(area : CaptureArea) -> Int {
let normalized = clamp_area(area)
normalized.width * normalized.height
}
///|
/// Intersect two capture areas and return the overlapping rectangle.
///
/// The function uses the rectangles exactly as provided. Normalize user input
/// with `clamp_area` first when you want negative coordinates or empty sizes to
/// be corrected before computing the overlap.
///
/// # Example
///
/// ```mbt check
/// test {
/// debug_inspect(
/// @screenshots.intersect({ x: 0, y: 0, width: 10, height: 10 }, {
/// x: 4,
/// y: 3,
/// width: 10,
/// height: 2,
/// }),
/// content="Some({ x: 4, y: 3, width: 6, height: 2 })",
/// )
/// }
/// ```
pub fn intersect(a : CaptureArea, b : CaptureArea) -> CaptureArea? {
let left = max_int(a.x, b.x)
let top = max_int(a.y, b.y)
let right = min_int(a.x + a.width, b.x + b.width)
let bottom = min_int(a.y + a.height, b.y + b.height)
if right <= left || bottom <= top {
None
} else {
Some({ x: left, y: top, width: right - left, height: bottom - top })
}
}