use std::rc::Rc;
use pax_runtime_api::{Property, Window};
use crate::api::math::{Generic, Transform2, Vector2};
use crate::api::{Axis, Size, Transform2D};
use crate::node_interface::NodeLocal;
use crate::ExpandedNode;
pub fn compute_tab(
node: &Rc<ExpandedNode>,
container_transform: Property<Transform2<NodeLocal, Window>>,
container_bounds: Property<(f64, f64)>,
) -> (
Property<Transform2<NodeLocal, Window>>,
Property<(f64, f64)>,
) {
let cp_container_bounds = container_bounds.clone();
let common_props = node.get_common_properties();
let common_props = common_props.borrow();
let cp_width = common_props.width.clone();
let cp_height = common_props.height.clone();
let deps = vec![
container_bounds.untyped(),
cp_width.untyped(),
cp_height.untyped(),
];
let bounds = Property::computed_with_name(
move || {
let p_bounds = cp_container_bounds.get();
let width = cp_width.get().evaluate(p_bounds, Axis::X);
let height = cp_height.get().evaluate(p_bounds, Axis::Y);
(width, height)
},
&deps,
&format!("bounds of node {}", node.id_chain[0]),
);
let cp_bounds = bounds.clone();
let cp_transform = common_props.transform.clone();
let cp_container_bounds = container_bounds.clone();
let cp_x = common_props.x.clone();
let cp_y = common_props.y.clone();
let cp_anchor_x = common_props.anchor_x.clone();
let cp_anchor_y = common_props.anchor_y.clone();
let cp_scale_x = common_props.scale_x.clone();
let cp_scale_y = common_props.scale_y.clone();
let cp_skew_x = common_props.skew_x.clone();
let cp_skew_y = common_props.skew_y.clone();
let cp_rotate = common_props.rotate.clone();
let size_props = [
&cp_x,
&cp_y,
&cp_anchor_x,
&cp_anchor_y,
&cp_scale_x,
&cp_scale_y,
]
.map(|v| v.as_ref().map(|p| p.untyped()))
.into_iter()
.flatten();
let other_props = [
cp_skew_x.as_ref().map(|p| p.untyped()),
cp_skew_y.as_ref().map(|p| p.untyped()),
cp_rotate.as_ref().map(|p| p.untyped()),
]
.into_iter()
.flatten();
let all_transform_deps: Vec<_> = size_props
.chain(other_props)
.chain(
[
cp_transform.untyped(),
cp_bounds.untyped(),
cp_container_bounds.untyped(),
container_transform.untyped(),
]
.into_iter(),
)
.collect();
let transform = Property::computed_with_name(
move || {
let node_transform_property_computed = {
cp_transform
.get()
.compute_transform2d_matrix(cp_bounds.get(), cp_container_bounds.get())
.cast_spaces::<NodeLocal, NodeLocal>()
};
let desugared_transform = {
let mut desugared_transform2d = Transform2D::default();
let translate = [
if let Some(ref val) = cp_x {
val.get().clone()
} else {
Size::ZERO()
},
if let Some(ref val) = cp_y {
val.get().clone()
} else {
Size::ZERO()
},
];
desugared_transform2d.translate = Some(translate);
let anchor = [
if let Some(ref val) = cp_anchor_x {
val.get().clone()
} else {
Size::ZERO()
},
if let Some(ref val) = cp_anchor_y {
val.get().clone()
} else {
Size::ZERO()
},
];
desugared_transform2d.anchor = Some(anchor);
let scale = [
if let Some(ref val) = cp_scale_x {
val.get().clone()
} else {
Size::Percent(crate::numeric::Numeric::from(100.0))
},
if let Some(ref val) = cp_scale_y {
val.get().clone()
} else {
Size::Percent(crate::numeric::Numeric::from(100.0))
},
];
desugared_transform2d.scale = Some(scale);
let skew = [
if let Some(ref val) = cp_skew_x {
val.get().to_float()
} else {
0.0
},
if let Some(ref val) = cp_skew_y {
val.get().to_float()
} else {
0.0
},
];
desugared_transform2d.skew = Some(skew);
let rotate = if let Some(ref val) = cp_rotate {
val.get().clone()
} else {
Default::default()
};
desugared_transform2d.rotate = Some(rotate);
desugared_transform2d
.compute_transform2d_matrix(cp_bounds.get(), container_bounds.get())
.cast_spaces::<NodeLocal, NodeLocal>()
};
container_transform.get() * desugared_transform * node_transform_property_computed
},
&all_transform_deps,
&format!("transform of node {}", node.id_chain[0]),
);
(transform, bounds)
}
pub trait ComputableTransform {
fn compute_transform2d_matrix(
&self,
node_size: (f64, f64),
container_bounds: (f64, f64),
) -> Transform2;
}
impl ComputableTransform for Transform2D {
fn compute_transform2d_matrix(
&self,
node_size: (f64, f64),
container_bounds: (f64, f64),
) -> Transform2 {
let anchor_transform = match &self.anchor {
Some(anchor) => Transform2::translate(Vector2::<Generic>::new(
match anchor[0] {
Size::Pixels(pix) => -pix.to_float(),
Size::Percent(per) => -node_size.0 * (per / 100.0),
Size::Combined(pix, per) => -pix.to_float() + (-node_size.0 * (per / 100.0)),
},
match anchor[1] {
Size::Pixels(pix) => -pix.to_float(),
Size::Percent(per) => -node_size.1 * (per / 100.0),
Size::Combined(pix, per) => -pix.to_float() + (-node_size.0 * (per / 100.0)),
},
)),
None => Transform2::default(),
};
let (scale_x, scale_y) = if let Some(scale) = self.scale {
(scale[0].expect_percent(), scale[1].expect_percent())
} else {
(1.0, 1.0)
};
let (skew_x, skew_y) = if let Some(skew) = self.skew {
(skew[0], skew[1])
} else {
(0.0, 0.0)
};
let (translate_x, translate_y) = if let Some(translate) = &self.translate {
(
translate[0].evaluate(container_bounds, Axis::X),
translate[1].evaluate(container_bounds, Axis::Y),
)
} else {
(0.0, 0.0)
};
let rotate_rads = if let Some(rotate) = &self.rotate {
rotate.to_float_0_1() * std::f64::consts::PI * 2.0
} else {
0.0
};
let cos_theta = rotate_rads.cos();
let sin_theta = rotate_rads.sin();
let a = scale_x * cos_theta - scale_y * skew_x * sin_theta;
let b = scale_x * sin_theta + scale_y * skew_x * cos_theta;
let c = -scale_y * sin_theta + scale_x * skew_y * cos_theta;
let d = scale_y * cos_theta + scale_x * skew_y * sin_theta;
let e = translate_x;
let f = translate_y;
let coeffs = [a, b, c, d, e, f];
let transform = Transform2::new(coeffs);
let previous_transform = match &self.previous {
Some(previous) => (*previous).compute_transform2d_matrix(node_size, container_bounds),
None => Transform2::default(),
};
transform * anchor_transform * previous_transform
}
}