mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 22:00:03 -04:00
Add layer transforms, interaction in layer (#3906)
⚠️ Removes `Context::translate_layer`, replacing it with a sticky `set_transform_layer` Adds the capability to scale layers. Allows interaction with scaled and transformed widgets inside transformed layers. I've also added a demo of how to have zooming and panning in a window (see the video below). This probably closes #1811. Having a panning and zooming container would just be creating a new `Area` with a new id, and applying zooming and panning with `ctx.transform_layer`. I've run the github workflow scripts in my repository, so hopefully the formatting and `cargo cranky` is satisfied. I'm not sure if all call sites where transforms would be relevant have been handled. This might also be missing are transforming clipping rects, but I'm not sure where / how to accomplish that. In the demo, the clipping rect is transformed to match, which seems to work. https://github.com/emilk/egui/assets/70821802/77e7e743-cdfe-402f-86e3-7744b3ee7b0f --------- Co-authored-by: tweoss <fchua@puffer5.stanford.edu> Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
@@ -36,6 +36,7 @@ mod rect;
|
||||
mod rect_transform;
|
||||
mod rot2;
|
||||
pub mod smart_aim;
|
||||
mod ts_transform;
|
||||
mod vec2;
|
||||
mod vec2b;
|
||||
|
||||
@@ -48,6 +49,7 @@ pub use {
|
||||
rect::*,
|
||||
rect_transform::*,
|
||||
rot2::*,
|
||||
ts_transform::*,
|
||||
vec2::*,
|
||||
vec2b::*,
|
||||
};
|
||||
|
||||
146
crates/emath/src/ts_transform.rs
Normal file
146
crates/emath/src/ts_transform.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
use crate::{Pos2, Rect, Vec2};
|
||||
|
||||
/// Linearly transforms positions via a translation, then a scaling.
|
||||
///
|
||||
/// [`TSTransform`] first scales points with the scaling origin at `0, 0`
|
||||
/// (the top left corner), then translates them.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
||||
pub struct TSTransform {
|
||||
/// Scaling applied first, scaled around (0, 0).
|
||||
pub scaling: f32,
|
||||
|
||||
/// Translation amount, applied after scaling.
|
||||
pub translation: Vec2,
|
||||
}
|
||||
|
||||
impl Eq for TSTransform {}
|
||||
|
||||
impl Default for TSTransform {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::IDENTITY
|
||||
}
|
||||
}
|
||||
|
||||
impl TSTransform {
|
||||
pub const IDENTITY: Self = Self {
|
||||
translation: Vec2::ZERO,
|
||||
scaling: 1.0,
|
||||
};
|
||||
|
||||
#[inline]
|
||||
/// Creates a new translation that first scales points around
|
||||
/// `(0, 0)`, then translates them.
|
||||
pub fn new(translation: Vec2, scaling: f32) -> Self {
|
||||
Self {
|
||||
translation,
|
||||
scaling,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_translation(translation: Vec2) -> Self {
|
||||
Self::new(translation, 1.0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_scaling(scaling: f32) -> Self {
|
||||
Self::new(Vec2::ZERO, scaling)
|
||||
}
|
||||
|
||||
/// Inverts the transform.
|
||||
///
|
||||
/// ```
|
||||
/// # use emath::{pos2, vec2, TSTransform};
|
||||
/// let p1 = pos2(2.0, 3.0);
|
||||
/// let p2 = pos2(12.0, 5.0);
|
||||
/// let ts = TSTransform::new(vec2(2.0, 3.0), 2.0);
|
||||
/// let inv = ts.inverse();
|
||||
/// assert_eq!(inv.mul_pos(p1), pos2(0.0, 0.0));
|
||||
/// assert_eq!(inv.mul_pos(p2), pos2(5.0, 1.0));
|
||||
///
|
||||
/// assert_eq!(ts.inverse().inverse(), ts);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn inverse(&self) -> Self {
|
||||
Self::new(-self.translation / self.scaling, 1.0 / self.scaling)
|
||||
}
|
||||
|
||||
/// Transforms the given coordinate.
|
||||
///
|
||||
/// ```
|
||||
/// # use emath::{pos2, vec2, TSTransform};
|
||||
/// let p1 = pos2(0.0, 0.0);
|
||||
/// let p2 = pos2(5.0, 1.0);
|
||||
/// let ts = TSTransform::new(vec2(2.0, 3.0), 2.0);
|
||||
/// assert_eq!(ts.mul_pos(p1), pos2(2.0, 3.0));
|
||||
/// assert_eq!(ts.mul_pos(p2), pos2(12.0, 5.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn mul_pos(&self, pos: Pos2) -> Pos2 {
|
||||
self.scaling * pos + self.translation
|
||||
}
|
||||
|
||||
/// Transforms the given rectangle.
|
||||
///
|
||||
/// ```
|
||||
/// # use emath::{pos2, vec2, Rect, TSTransform};
|
||||
/// let rect = Rect::from_min_max(pos2(5.0, 5.0), pos2(15.0, 10.0));
|
||||
/// let ts = TSTransform::new(vec2(1.0, 0.0), 3.0);
|
||||
/// let transformed = ts.mul_rect(rect);
|
||||
/// assert_eq!(transformed.min, pos2(16.0, 15.0));
|
||||
/// assert_eq!(transformed.max, pos2(46.0, 30.0));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn mul_rect(&self, rect: Rect) -> Rect {
|
||||
Rect {
|
||||
min: self.mul_pos(rect.min),
|
||||
max: self.mul_pos(rect.max),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms the position.
|
||||
impl std::ops::Mul<Pos2> for TSTransform {
|
||||
type Output = Pos2;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, pos: Pos2) -> Pos2 {
|
||||
self.mul_pos(pos)
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms the rectangle.
|
||||
impl std::ops::Mul<Rect> for TSTransform {
|
||||
type Output = Rect;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, rect: Rect) -> Rect {
|
||||
self.mul_rect(rect)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul<Self> for TSTransform {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
/// Applies the right hand side transform, then the left hand side.
|
||||
///
|
||||
/// ```
|
||||
/// # use emath::{TSTransform, vec2};
|
||||
/// let ts1 = TSTransform::new(vec2(1.0, 0.0), 2.0);
|
||||
/// let ts2 = TSTransform::new(vec2(-1.0, -1.0), 3.0);
|
||||
/// let ts_combined = TSTransform::new(vec2(2.0, -1.0), 6.0);
|
||||
/// assert_eq!(ts_combined, ts2 * ts1);
|
||||
/// ```
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
// Apply rhs first.
|
||||
Self {
|
||||
scaling: self.scaling * rhs.scaling,
|
||||
translation: self.translation + self.scaling * rhs.translation,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user