From 63ea41556a2df79728f623cf3212c16f0f3f7910 Mon Sep 17 00:00:00 2001 From: Jerome Humbert Date: Fri, 26 Apr 2024 20:24:01 +0100 Subject: [PATCH] Enable lenses to avoid change detection (#124) Add the ability for all `Lens`es to avoid triggering change detection if they don't make any actual change to their target (component or asset). This change modifies the signature of `Lerp::lerp()` to take `&mut dyn Targetable` instead of `&mut T`. The `Targetable` acts as a `Mut`, marking the target as changed only if actually dereferenced mutably. Note that we cannot use `Mut` directly because we can't obtain a `Mut` without marking the asset as mutable; see bevyengine/bevy#13104. Fixes #91 --- CHANGELOG.md | 22 +++ src/lens.rs | 497 +++++++++++++++++++++++++++++++++++++++++------ src/lib.rs | 52 ++++- src/plugin.rs | 158 ++++++++++++--- src/tweenable.rs | 82 +++++++- 5 files changed, 722 insertions(+), 89 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b875e0b..dd2d7a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed + +- `Lens::lerp()` now takes a `&mut dyn Targetable` instead of `&mut T`. + This ensures the lens can skip change detection if needed. + `Targetable` conceptually acts like a `Mut`, but allows encapsulating assets too. + There should be no other change than the function signature to upgrade custom lenses, + because `dyn Targetable` now implements `Defer` and `DeferMut`, so can be used in place of `&mut T`. +- `AssetTarget::new()` now takes a simple `Mut>` instead of `ResMut>`. + +### Fixed + +- Fixed change detection such that lenses which do not dereference their target + do not unconditionally mark that target (component or asset) as changed from the point of view of ECS. (#91) + +### Added + +- Added `Targetable::target(&self) -> &T`. +- `dyn Targetable` now implements `Defer` and `DeferMut`, so can be used in place of `&T` and `&mut T`. +- Added `ComponentTarget::to_mut(&mut self) -> Mut<'_, T>` to "reborrow" the component target as a `Mut`. + ## [0.10.0] - 2024-02-27 ### Changed diff --git a/src/lens.rs b/src/lens.rs index 0afd1fc..227d94e 100644 --- a/src/lens.rs +++ b/src/lens.rs @@ -37,6 +37,8 @@ use bevy::prelude::*; +use crate::Targetable; + /// A lens over a subset of a component. /// /// The lens takes a `target` component or asset from a query, as a mutable @@ -60,7 +62,7 @@ use bevy::prelude::*; /// struct MyStruct(f32); /// /// impl Lens for MyLens { -/// fn lerp(&mut self, target: &mut MyStruct, ratio: f32) { +/// fn lerp(&mut self, target: &mut dyn Targetable, ratio: f32) { /// target.0 = self.start + (self.end - self.start) * ratio; /// } /// } @@ -71,7 +73,7 @@ pub trait Lens { /// `ratio`. The `target` component or asset is mutated in place. The /// implementation decides which fields are interpolated, and performs /// the animation in-place, overwriting the target. - fn lerp(&mut self, target: &mut T, ratio: f32); + fn lerp(&mut self, target: &mut dyn Targetable, ratio: f32); } /// A lens to manipulate the [`color`] field of a section of a [`Text`] @@ -92,7 +94,7 @@ pub struct TextColorLens { #[cfg(feature = "bevy_text")] impl Lens for TextColorLens { - fn lerp(&mut self, target: &mut Text, ratio: f32) { + fn lerp(&mut self, target: &mut dyn Targetable, ratio: f32) { use crate::ColorLerper as _; // Note: Add for Color affects alpha, but not Mul. So use Vec4 for @@ -118,7 +120,7 @@ pub struct TransformPositionLens { } impl Lens for TransformPositionLens { - fn lerp(&mut self, target: &mut Transform, ratio: f32) { + fn lerp(&mut self, target: &mut dyn Targetable, ratio: f32) { let value = self.start + (self.end - self.start) * ratio; target.translation = value; } @@ -150,7 +152,7 @@ pub struct TransformRotationLens { } impl Lens for TransformRotationLens { - fn lerp(&mut self, target: &mut Transform, ratio: f32) { + fn lerp(&mut self, target: &mut dyn Targetable, ratio: f32) { target.rotation = self.start.slerp(self.end, ratio); } } @@ -176,7 +178,7 @@ pub struct TransformRotateXLens { } impl Lens for TransformRotateXLens { - fn lerp(&mut self, target: &mut Transform, ratio: f32) { + fn lerp(&mut self, target: &mut dyn Targetable, ratio: f32) { let angle = (self.end - self.start).mul_add(ratio, self.start); target.rotation = Quat::from_rotation_x(angle); } @@ -203,7 +205,7 @@ pub struct TransformRotateYLens { } impl Lens for TransformRotateYLens { - fn lerp(&mut self, target: &mut Transform, ratio: f32) { + fn lerp(&mut self, target: &mut dyn Targetable, ratio: f32) { let angle = (self.end - self.start).mul_add(ratio, self.start); target.rotation = Quat::from_rotation_y(angle); } @@ -230,7 +232,7 @@ pub struct TransformRotateZLens { } impl Lens for TransformRotateZLens { - fn lerp(&mut self, target: &mut Transform, ratio: f32) { + fn lerp(&mut self, target: &mut dyn Targetable, ratio: f32) { let angle = (self.end - self.start).mul_add(ratio, self.start); target.rotation = Quat::from_rotation_z(angle); } @@ -263,7 +265,7 @@ pub struct TransformRotateAxisLens { } impl Lens for TransformRotateAxisLens { - fn lerp(&mut self, target: &mut Transform, ratio: f32) { + fn lerp(&mut self, target: &mut dyn Targetable, ratio: f32) { let angle = (self.end - self.start).mul_add(ratio, self.start); target.rotation = Quat::from_axis_angle(self.axis, angle); } @@ -282,7 +284,7 @@ pub struct TransformScaleLens { } impl Lens for TransformScaleLens { - fn lerp(&mut self, target: &mut Transform, ratio: f32) { + fn lerp(&mut self, target: &mut dyn Targetable, ratio: f32) { let value = self.start + (self.end - self.start) * ratio; target.scale = value; } @@ -314,7 +316,7 @@ fn lerp_val(start: &Val, end: &Val, ratio: f32) -> Val { #[cfg(feature = "bevy_ui")] impl Lens