1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
use crate::api::{Axis, Size, Transform2D};
use crate::{ExpandedNode, TransformAndBounds};
use kurbo::Affine;
/// For the `current_expanded_node` attached to `ptc`, calculates and returns a new [`crate::rendering::TransformAndBounds`] a.k.a. "tab".
/// Intended as a helper method to be called during properties computation, for creating a new tab to attach to `ptc` for downstream calculations.
pub fn compute_tab(node: &ExpandedNode, container_tab: &TransformAndBounds) -> TransformAndBounds {
//get the size of this node (calc'd or otherwise) and use
//it as the new accumulated bounds: both for this node's children (their parent container bounds)
//and for this node itself (e.g. for specifying the size of a Rectangle node)
let new_accumulated_bounds_and_current_node_size =
{ node.get_size_computed(container_tab.bounds) };
let node_transform_property_computed = {
node.get_common_properties()
.borrow()
.transform
.get()
.compute_transform2d_matrix(
new_accumulated_bounds_and_current_node_size.clone(),
container_tab.bounds,
)
};
// From a combination of the sugared TemplateNodeDefinition properties like `width`, `height`, `x`, `y`, `scale_x`, etc.
let desugared_transform = {
//Extract common_properties, pack into Transform2D, decompose / compute, and combine with node_computed_transform
let comm = node.get_common_properties();
let comm = comm.borrow();
let mut desugared_transform2d = Transform2D::default();
let translate = [
if let Some(ref val) = comm.x {
val.get().clone()
} else {
Size::ZERO()
},
if let Some(ref val) = comm.y {
val.get().clone()
} else {
Size::ZERO()
},
];
desugared_transform2d.translate = Some(translate);
let anchor = [
if let Some(ref val) = comm.anchor_x {
val.get().clone()
} else {
Size::ZERO()
},
if let Some(ref val) = comm.anchor_y {
val.get().clone()
} else {
Size::ZERO()
},
];
desugared_transform2d.anchor = Some(anchor);
let scale = [
if let Some(ref val) = comm.scale_x {
val.get().clone()
} else {
Size::Percent(crate::numeric::Numeric::from(100.0))
},
if let Some(ref val) = comm.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) = comm.skew_x {
val.get().get_as_float()
} else {
0.0
},
if let Some(ref val) = comm.skew_y {
val.get().get_as_float()
} else {
0.0
},
];
desugared_transform2d.skew = Some(skew);
let rotate = if let Some(ref val) = comm.rotate {
val.get().clone()
} else {
crate::api::Rotation::ZERO()
};
desugared_transform2d.rotate = Some(rotate);
desugared_transform2d.compute_transform2d_matrix(
new_accumulated_bounds_and_current_node_size.clone(),
container_tab.bounds,
)
};
let new_accumulated_transform =
container_tab.transform * desugared_transform * node_transform_property_computed;
// let new_scroller_normalized_accumulated_transform =
// accumulated_scroller_normalized_transform
// * desugared_transform
// * node_transform_property_computed;
// rtc.transform_scroller_reset = new_scroller_normalized_accumulated_transform.clone();
TransformAndBounds {
transform: new_accumulated_transform,
bounds: new_accumulated_bounds_and_current_node_size,
}
}
pub trait ComputableTransform {
fn compute_transform2d_matrix(
&self,
node_size: (f64, f64),
container_bounds: (f64, f64),
) -> Affine;
}
impl ComputableTransform for Transform2D {
//Distinction of note: scale, translate, rotate, anchor, and align are all AUTHOR-TIME properties
// node_size and container_bounds are (computed) RUNTIME properties
fn compute_transform2d_matrix(
&self,
node_size: (f64, f64),
container_bounds: (f64, f64),
) -> Affine {
//Three broad strokes:
// a.) compute anchor
// b.) decompose "vanilla" affine matrix
// c.) combine with previous transform chain (assembled via multiplication of two Transform2Ds, e.g. in PAXEL)
// Compute anchor
let anchor_transform = match &self.anchor {
Some(anchor) => Affine::translate((
match anchor[0] {
Size::Pixels(pix) => -pix.get_as_float(),
Size::Percent(per) => -node_size.0 * (per / 100.0),
Size::Combined(pix, per) => {
-pix.get_as_float() + (-node_size.0 * (per / 100.0))
}
},
match anchor[1] {
Size::Pixels(pix) => -pix.get_as_float(),
Size::Percent(per) => -node_size.1 * (per / 100.0),
Size::Combined(pix, per) => {
-pix.get_as_float() + (-node_size.0 * (per / 100.0))
}
},
)),
//No anchor applied: treat as 0,0; identity matrix
None => Affine::default(),
};
//decompose vanilla affine matrix and pack into `Affine`
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.get_as_radians()
} else {
0.0
};
let cos_theta = rotate_rads.cos();
let sin_theta = rotate_rads.sin();
// Elements for a combined scale and rotation
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;
// Translation
let e = translate_x;
let f = translate_y;
let coeffs = [a, b, c, d, e, f];
let transform = Affine::new(coeffs);
// Compute and combine previous_transform
let previous_transform = match &self.previous {
Some(previous) => (*previous).compute_transform2d_matrix(node_size, container_bounds),
None => Affine::default(),
};
transform * anchor_transform * previous_transform
}
}