Fix cursor hotspot out of bounds when flipping (#17571)

# Objective

- Fix off by one error introduced in
https://github.com/bevyengine/bevy/pull/17540 causing:

```
Cursor image StrongHandle<Image>{ id: Index(AssetIndex { generation: 0, index: 3 }), path: Some(cursors/kenney_crosshairPack/Tilesheet/crosshairs_tilesheet_white.png) } is invalid: The specified hotspot (64, 64) is outside the image bounds (64x64).
```

- First PR commit and run shows the bug:
https://github.com/bevyengine/bevy/actions/runs/13009405866/job/36283507530?pr=17571
- Second PR commit fixes it.

## Solution

- Hotspot coordinates are 0-indexed, so we need to subtract 1 from the
width and height.

## Testing

- Fix the tests which included the off-by-one error in their expected
values.
- Consolidate the tests into a single test for brevity.
- Test round trip transform to ensure we can "undo" to get back to the
original value.
- Add a specific bounds test.
- Ran the example again and observed there are no more error logs:
`cargo run --example custom_cursor_image --features=custom_cursor`.
This commit is contained in:
mgi388 2025-02-03 05:22:34 +11:00 committed by GitHub
parent 469b218f20
commit 756948e311
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -204,11 +204,16 @@ pub(crate) fn transform_hotspot(
) -> (u16, u16) {
let hotspot_x = hotspot.0 as f32;
let hotspot_y = hotspot.1 as f32;
let (width, height) = (rect.width(), rect.height());
let hotspot_x = if flip_x { width - hotspot_x } else { hotspot_x };
let hotspot_x = if flip_x {
(width - 1.0).max(0.0) - hotspot_x
} else {
hotspot_x
};
let hotspot_y = if flip_y {
height - hotspot_y
(height - 1.0).max(0.0) - hotspot_y
} else {
hotspot_y
};
@ -576,46 +581,26 @@ mod tests {
);
#[test]
fn test_transform_hotspot_no_flip() {
let hotspot = (10, 20);
let rect = Rect {
min: Vec2::ZERO,
max: Vec2::new(100.0, 200.0),
};
let transformed = transform_hotspot(hotspot, false, false, rect);
assert_eq!(transformed, (10, 20));
}
fn test_transform_hotspot() {
fn test(hotspot: (u16, u16), flip_x: bool, flip_y: bool, rect: Rect, expected: (u16, u16)) {
let transformed = transform_hotspot(hotspot, flip_x, flip_y, rect);
assert_eq!(transformed, expected);
#[test]
fn test_transform_hotspot_flip_x() {
let hotspot = (10, 20);
let rect = Rect {
min: Vec2::ZERO,
max: Vec2::new(100.0, 200.0),
};
let transformed = transform_hotspot(hotspot, true, false, rect);
assert_eq!(transformed, (90, 20));
}
// Round-trip test: Applying the same transformation again should
// reverse it.
let transformed = transform_hotspot(transformed, flip_x, flip_y, rect);
assert_eq!(transformed, hotspot);
}
#[test]
fn test_transform_hotspot_flip_y() {
let hotspot = (10, 20);
let rect = Rect {
min: Vec2::ZERO,
max: Vec2::new(100.0, 200.0),
};
let transformed = transform_hotspot(hotspot, false, true, rect);
assert_eq!(transformed, (10, 180));
}
#[test]
fn test_transform_hotspot_flip_both() {
let hotspot = (10, 20);
let rect = Rect {
min: Vec2::ZERO,
max: Vec2::new(100.0, 200.0),
};
let transformed = transform_hotspot(hotspot, true, true, rect);
assert_eq!(transformed, (90, 180));
test((10, 20), false, false, rect, (10, 20)); // no flip
test((10, 20), true, false, rect, (89, 20)); // flip X
test((10, 20), false, true, rect, (10, 179)); // flip Y
test((10, 20), true, true, rect, (89, 179)); // flip both
test((0, 0), true, true, rect, (99, 199)); // flip both (bounds check)
}
}