Skip to content

Commit

Permalink
Add Rescaler component (#275)
Browse files Browse the repository at this point in the history
  • Loading branch information
wkozyra95 authored Nov 30, 2023
1 parent fb27e5e commit 90475d6
Show file tree
Hide file tree
Showing 28 changed files with 1,051 additions and 34 deletions.
4 changes: 4 additions & 0 deletions compositor_render/src/scene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mod components;
mod image_component;
mod input_stream_component;
mod layout;
mod rescaler_component;
mod scene_state;
mod shader_component;
mod text_component;
Expand All @@ -52,6 +53,7 @@ pub enum Component {
Text(TextComponent),
View(ViewComponent),
Tiles(TilesComponent),
Rescaler(RescalerComponent),
}

/// Stateful version of a `Component`. Represents the same element as
Expand Down Expand Up @@ -133,6 +135,7 @@ impl StatefulComponent {
StatefulComponent::Layout(layout) => match layout {
StatefulLayoutComponent::View(view) => view.intermediate_node(),
StatefulLayoutComponent::Tiles(tiles) => tiles.intermediate_node(),
StatefulLayoutComponent::Rescaler(rescaler) => rescaler.intermediate_node(),
},
}
}
Expand All @@ -153,6 +156,7 @@ impl Component {
Component::Text(text) => text.stateful_component(ctx),
Component::View(view) => view.stateful_component(ctx),
Component::Tiles(tiles) => tiles.stateful_component(ctx),
Component::Rescaler(rescaler) => rescaler.stateful_component(ctx),
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions compositor_render/src/scene/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,25 @@ pub enum HorizontalPosition {
RightOffset(f32),
}

#[derive(Debug, Clone)]
pub struct RescalerComponent {
pub id: Option<ComponentId>,
pub child: Box<Component>,

pub position: Position,
pub transition: Option<Transition>,

pub mode: ResizeMode,
pub horizontal_align: HorizontalAlign,
pub vertical_align: VerticalAlign,
}

#[derive(Debug, Clone, Copy)]
pub enum ResizeMode {
Fit,
Fill,
}

#[derive(Debug, Clone)]
pub struct TilesComponent {
pub id: Option<ComponentId>,
Expand Down
13 changes: 10 additions & 3 deletions compositor_render/src/scene/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ use compositor_common::scene::Resolution;
use crate::transformations::layout::{self, LayoutContent, NestedLayout};

use super::{
tiles_component::StatefulTilesComponent, view_component::StatefulViewComponent,
AbsolutePosition, ComponentId, HorizontalPosition, Position, Size, StatefulComponent,
VerticalPosition,
rescaler_component::StatefulRescalerComponent, tiles_component::StatefulTilesComponent,
view_component::StatefulViewComponent, AbsolutePosition, ComponentId, HorizontalPosition,
Position, Size, StatefulComponent, VerticalPosition,
};

#[derive(Debug, Clone)]
pub(super) enum StatefulLayoutComponent {
View(StatefulViewComponent),
Tiles(StatefulTilesComponent),
Rescaler(StatefulRescalerComponent),
}

#[derive(Debug)]
Expand Down Expand Up @@ -43,41 +44,47 @@ impl StatefulLayoutComponent {
match self {
StatefulLayoutComponent::View(view) => view.layout(size, pts),
StatefulLayoutComponent::Tiles(tiles) => tiles.layout(size, pts),
StatefulLayoutComponent::Rescaler(rescaler) => rescaler.layout(size, pts),
}
}

pub(super) fn position(&self, pts: Duration) -> Position {
match self {
StatefulLayoutComponent::View(view) => view.position(pts),
StatefulLayoutComponent::Tiles(tiles) => tiles.position(pts),
StatefulLayoutComponent::Rescaler(rescaler) => rescaler.position(pts),
}
}

pub(crate) fn component_id(&self) -> Option<&ComponentId> {
match self {
StatefulLayoutComponent::View(view) => view.component_id(),
StatefulLayoutComponent::Tiles(tiles) => tiles.component_id(),
StatefulLayoutComponent::Rescaler(rescaler) => rescaler.component_id(),
}
}

pub(crate) fn component_type(&self) -> &'static str {
match self {
StatefulLayoutComponent::View(_) => "View",
StatefulLayoutComponent::Tiles(_) => "Tiles",
StatefulLayoutComponent::Rescaler(_) => "Rescaler",
}
}

pub(super) fn children(&self) -> Vec<&StatefulComponent> {
match self {
StatefulLayoutComponent::View(view) => view.children(),
StatefulLayoutComponent::Tiles(tiles) => tiles.children(),
StatefulLayoutComponent::Rescaler(rescaler) => rescaler.children(),
}
}

pub(super) fn children_mut(&mut self) -> Vec<&mut StatefulComponent> {
match self {
StatefulLayoutComponent::View(view) => view.children_mut(),
StatefulLayoutComponent::Tiles(tiles) => tiles.children_mut(),
StatefulLayoutComponent::Rescaler(rescaler) => rescaler.children_mut(),
}
}

Expand Down
144 changes: 144 additions & 0 deletions compositor_render/src/scene/rescaler_component.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
use std::{ops::Add, time::Duration};

use compositor_common::util::{
align::{HorizontalAlign, VerticalAlign},
ContinuousValue, InterpolationState,
};

use crate::transformations::layout::NestedLayout;

use super::{
components::RescalerComponent, layout::StatefulLayoutComponent, scene_state::BuildStateTreeCtx,
Component, ComponentId, IntermediateNode, Position, ResizeMode, SceneError, Size,
StatefulComponent, Transition,
};

mod interpolation;
mod layout;

#[derive(Debug, Clone)]
pub(super) struct StatefulRescalerComponent {
start: Option<RescalerComponentParam>,
end: RescalerComponentParam,
transition: Option<Transition>,
child: Box<StatefulComponent>,
start_pts: Duration,
}

#[derive(Debug, Clone)]
struct RescalerComponentParam {
id: Option<ComponentId>,

position: Position,
mode: ResizeMode,
horizontal_align: HorizontalAlign,
vertical_align: VerticalAlign,
}

impl StatefulRescalerComponent {
/// Generate state of the component for particular pts value.
fn transition_snapshot(&self, pts: Duration) -> RescalerComponentParam {
let (Some(transition), Some(start)) = (self.transition, &self.start) else {
return self.end.clone();
};
let interpolation_progress = InterpolationState(f64::min(
1.0,
(pts.as_secs_f64() - self.start_pts.as_secs_f64()) / transition.duration.as_secs_f64(),
));
ContinuousValue::interpolate(start, &self.end, interpolation_progress)
}

fn remaining_transition_duration(&self, pts: Duration) -> Option<Duration> {
self.transition.and_then(|transition| {
if self.start_pts + transition.duration > pts {
None
} else {
self.start_pts.add(transition.duration).checked_sub(pts)
}
})
}

pub(super) fn children(&self) -> Vec<&StatefulComponent> {
vec![&self.child]
}

pub(super) fn children_mut(&mut self) -> Vec<&mut StatefulComponent> {
vec![&mut self.child]
}

pub(super) fn position(&self, pts: Duration) -> Position {
self.transition_snapshot(pts).position
}

pub(super) fn component_id(&self) -> Option<&ComponentId> {
self.end.id.as_ref()
}

pub(super) fn intermediate_node(&self) -> IntermediateNode {
let children = {
let node = self.child.intermediate_node();
match node {
IntermediateNode::Layout { root: _, children } => children,
_ => vec![node],
}
};

IntermediateNode::Layout {
root: StatefulLayoutComponent::Rescaler(self.clone()),
children,
}
}

pub(super) fn layout(&self, size: Size, pts: Duration) -> NestedLayout {
self.transition_snapshot(pts).layout(size, &self.child, pts)
}
}

impl RescalerComponent {
pub(super) fn stateful_component(
self,
ctx: &BuildStateTreeCtx,
) -> Result<StatefulComponent, SceneError> {
let previous_state = self
.id
.as_ref()
.and_then(|id| ctx.prev_state.get(id))
.and_then(|component| match component {
StatefulComponent::Layout(StatefulLayoutComponent::Rescaler(view_state)) => {
Some(view_state)
}
_ => None,
});

// TODO: to handle cases like transition from top to bottom this view needs
// to be further processed to use the same type of coordinates as end
let start = previous_state.map(|state| state.transition_snapshot(ctx.last_render_pts));
// TODO: this is incorrect for non linear transformations
let transition = self.transition.or_else(|| {
let Some(previous_state) = previous_state else {
return None;
};
let Some(duration) = previous_state.remaining_transition_duration(ctx.last_render_pts)
else {
return None;
};
previous_state.transition.map(|_| Transition { duration })
});
let view = StatefulRescalerComponent {
start,
end: RescalerComponentParam {
id: self.id,
position: self.position,
mode: self.mode,
horizontal_align: self.horizontal_align,
vertical_align: self.vertical_align,
},
transition,
child: Box::new(Component::stateful_component(*self.child, ctx)?),
start_pts: ctx.last_render_pts,
};
Ok(StatefulComponent::Layout(
StatefulLayoutComponent::Rescaler(view),
))
}
}
15 changes: 15 additions & 0 deletions compositor_render/src/scene/rescaler_component/interpolation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use compositor_common::util::{ContinuousValue, InterpolationState};

use super::RescalerComponentParam;

impl ContinuousValue for RescalerComponentParam {
fn interpolate(start: &Self, end: &Self, state: InterpolationState) -> Self {
Self {
id: end.id.clone(),
position: ContinuousValue::interpolate(&start.position, &end.position, state),
mode: end.mode,
horizontal_align: end.horizontal_align,
vertical_align: end.vertical_align,
}
}
}
Loading

0 comments on commit 90475d6

Please sign in to comment.