Skip to content

Commit

Permalink
Transform cursor hotspot when flipping image
Browse files Browse the repository at this point in the history
  • Loading branch information
mgi388 committed Jan 26, 2025
1 parent 39a1e2b commit 4a4f5ad
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 4 deletions.
11 changes: 7 additions & 4 deletions crates/bevy_winit/src/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
use crate::{
custom_cursor::{
calculate_effective_rect, extract_and_transform_rgba_pixels, extract_rgba_pixels,
CustomCursorPlugin,
transform_hotspot, CustomCursorPlugin,
},
state::{CustomCursorCache, CustomCursorCacheKey},
WinitCustomCursor,
Expand Down Expand Up @@ -124,10 +124,13 @@ fn update_cursors(
let (rect, needs_sub_image) =
calculate_effective_rect(&texture_atlases, image, texture_atlas, rect);

let maybe_rgba = if *flip_x || *flip_y || needs_sub_image {
extract_and_transform_rgba_pixels(image, *flip_x, *flip_y, rect)
let (maybe_rgba, hotspot) = if *flip_x || *flip_y || needs_sub_image {
(
extract_and_transform_rgba_pixels(image, *flip_x, *flip_y, rect),
transform_hotspot(*hotspot, *flip_x, *flip_y, rect),
)
} else {
extract_rgba_pixels(image)
(extract_rgba_pixels(image), *hotspot)
};

let Some(rgba) = maybe_rgba else {
Expand Down
74 changes: 74 additions & 0 deletions crates/bevy_winit/src/custom_cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,16 @@ pub struct CustomCursorImage {
/// An optional texture atlas used to render the image.
pub texture_atlas: Option<TextureAtlas>,
/// Whether the image should be flipped along its x-axis.
///
/// If true, the cursor's hotspot will be automatically flipped along with
/// the image. You don't need to adjust the `hotspot` field to account for
/// the flip.
pub flip_x: bool,
/// Whether the image should be flipped along its y-axis.
///
/// If true, the cursor's hotspot will be automatically flipped along with
/// the image. You don't need to adjust the `hotspot` field to account for
/// the flip.
pub flip_y: bool,
/// An optional rectangle representing the region of the image to render,
/// instead of rendering the full image. This is an easy one-off alternative
Expand Down Expand Up @@ -184,6 +192,28 @@ pub(crate) fn extract_and_transform_rgba_pixels(
Some(sub_image_data)
}

/// Transforms the `hotspot` coordinates based on whether the image is flipped
/// or not. The `rect` is used to determine the image's dimensions.
pub(crate) fn transform_hotspot(
hotspot: (u16, u16),
flip_x: bool,
flip_y: bool,
rect: Rect,
) -> (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_y = if flip_y {
height - hotspot_y
} else {
hotspot_y
};

(hotspot_x as u16, hotspot_y as u16)
}

#[cfg(test)]
mod tests {
use bevy_app::App;
Expand Down Expand Up @@ -542,4 +572,48 @@ mod tests {
0, 255, 255, 255, // Cyan
]
);

#[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));
}

#[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));
}

#[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));
}
}

0 comments on commit 4a4f5ad

Please sign in to comment.