1use azul_css::props::{
11 basic::{
12 pixel::PT_TO_PX,
13 PixelValue, SizeMetric,
14 },
15 layout::dimensions::{CalcAstItem, CalcAstItemVec},
16};
17
18pub(super) const PX_PER_INCH: f32 = 96.0;
20pub(super) const CM_PER_INCH: f32 = 2.54;
22pub(super) const MM_PER_INCH: f32 = 25.4;
24
25#[derive(Debug, Clone)]
31#[repr(C)]
32pub struct CalcResolveContext {
33 pub items: CalcAstItemVec,
35 pub em_size: f32,
37 pub rem_size: f32,
39}
40#[allow(variant_size_differences)]
44#[derive(Clone, Debug)]
46enum CalcFlatItem {
47 Num(f32),
48 Op(CalcOp),
49}
50
51#[derive(Clone, Copy, Debug, PartialEq)]
53enum CalcOp {
54 Add,
55 Sub,
56 Mul,
57 Div,
58}
59
60#[inline(never)] #[must_use] pub fn evaluate_calc(ctx: &CalcResolveContext, basis: f32) -> f32 {
64 evaluate_calc_ast(ctx.items.as_slice(), basis, ctx.em_size, ctx.rem_size)
65}
66
67#[inline(never)] fn evaluate_calc_ast(
79 items: &[CalcAstItem],
80 basis: f32,
81 em_size: f32,
82 rem_size: f32,
83) -> f32 {
84 let mut flat: Vec<CalcFlatItem> = Vec::with_capacity(items.len());
86 let mut i = 0;
87 while i < items.len() {
88 match &items[i] {
89 CalcAstItem::Value(pv) => {
90 flat.push(CalcFlatItem::Num(resolve_pixel_value(
91 pv, basis, em_size, rem_size,
92 )));
93 }
94 CalcAstItem::Add => flat.push(CalcFlatItem::Op(CalcOp::Add)),
95 CalcAstItem::Sub => flat.push(CalcFlatItem::Op(CalcOp::Sub)),
96 CalcAstItem::Mul => flat.push(CalcFlatItem::Op(CalcOp::Mul)),
97 CalcAstItem::Div => flat.push(CalcFlatItem::Op(CalcOp::Div)),
98 CalcAstItem::BraceOpen => {
99 let start = i + 1;
101 let mut depth = 1u32;
102 let mut j = start;
103 while j < items.len() && depth > 0 {
104 match &items[j] {
105 CalcAstItem::BraceOpen => depth += 1,
106 CalcAstItem::BraceClose => depth -= 1,
107 _ => {}
108 }
109 if depth > 0 {
110 j += 1;
111 }
112 }
113 let sub_val = evaluate_calc_ast(&items[start..j], basis, em_size, rem_size);
115 flat.push(CalcFlatItem::Num(sub_val));
116 i = j; }
118 CalcAstItem::BraceClose => { }
119 }
120 i += 1;
121 }
122
123 let mut pass2: Vec<CalcFlatItem> = Vec::with_capacity(flat.len());
125 let mut k = 0;
126 while k < flat.len() {
127 if let CalcFlatItem::Op(op @ (CalcOp::Mul | CalcOp::Div)) = &flat[k] {
128 if let (Some(CalcFlatItem::Num(lhs)), Some(CalcFlatItem::Num(rhs))) =
130 (pass2.last(), flat.get(k + 1))
131 {
132 let result = match op {
133 CalcOp::Mul => lhs * rhs,
134 CalcOp::Div => {
135 if *rhs == 0.0 {
136 0.0
137 } else {
138 lhs / rhs
139 }
140 }
141 _ => unreachable!(),
142 };
143 *pass2.last_mut().unwrap() = CalcFlatItem::Num(result);
144 k += 2; continue;
146 }
147 }
148 pass2.push(flat[k].clone());
149 k += 1;
150 }
151
152 let mut result = match pass2.first() {
154 Some(CalcFlatItem::Num(v)) => *v,
155 _ => return 0.0,
156 };
157 let mut m = 1;
158 while m < pass2.len() {
159 if let (CalcFlatItem::Op(op), Some(CalcFlatItem::Num(rhs))) =
160 (&pass2[m], pass2.get(m + 1))
161 {
162 match op {
163 CalcOp::Add => result += rhs,
164 CalcOp::Sub => result -= rhs,
165 _ => {} }
167 m += 2;
168 } else {
169 m += 1;
170 }
171 }
172
173 result
174}
175
176#[must_use] pub fn resolve_pixel_value(
187 pv: &PixelValue,
188 basis: f32,
189 em_size: f32,
190 rem_size: f32,
191) -> f32 {
192 match pv.metric {
193 SizeMetric::Px => pv.number.get(),
194 SizeMetric::Pt => pv.number.get() * PT_TO_PX,
195 SizeMetric::In => pv.number.get() * PX_PER_INCH,
196 SizeMetric::Cm => pv.number.get() * PX_PER_INCH / CM_PER_INCH,
197 SizeMetric::Mm => pv.number.get() * PX_PER_INCH / MM_PER_INCH,
198 SizeMetric::Em => pv.number.get() * em_size,
199 SizeMetric::Rem => pv.number.get() * rem_size,
200 SizeMetric::Percent => basis * (pv.number.get() / 100.0),
201 SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => {
202 pv.number.get()
204 }
205 }
206}
207
208#[inline(never)] #[must_use] pub fn resolve_pixel_value_with_viewport(
211 pv: &PixelValue,
212 basis: f32,
213 em_size: f32,
214 rem_size: f32,
215 viewport_width: f32,
216 viewport_height: f32,
217) -> f32 {
218 match pv.metric {
219 SizeMetric::Vw => pv.number.get() / 100.0 * viewport_width,
220 SizeMetric::Vh => pv.number.get() / 100.0 * viewport_height,
221 SizeMetric::Vmin => pv.number.get() / 100.0 * viewport_width.min(viewport_height),
222 SizeMetric::Vmax => pv.number.get() / 100.0 * viewport_width.max(viewport_height),
223 _ => resolve_pixel_value(pv, basis, em_size, rem_size),
224 }
225}
226
227#[must_use] pub fn resolve_pixel_value_no_percent(
229 pv: &PixelValue,
230 em_size: f32,
231 rem_size: f32,
232) -> Option<f32> {
233 match pv.metric {
234 SizeMetric::Px => Some(pv.number.get()),
235 SizeMetric::Pt => Some(pv.number.get() * PT_TO_PX),
236 SizeMetric::In => Some(pv.number.get() * PX_PER_INCH),
237 SizeMetric::Cm => Some(pv.number.get() * PX_PER_INCH / CM_PER_INCH),
238 SizeMetric::Mm => Some(pv.number.get() * PX_PER_INCH / MM_PER_INCH),
239 SizeMetric::Em => Some(pv.number.get() * em_size),
240 SizeMetric::Rem => Some(pv.number.get() * rem_size),
241 SizeMetric::Percent
242 | SizeMetric::Vw
243 | SizeMetric::Vh
244 | SizeMetric::Vmin
245 | SizeMetric::Vmax => None,
246 }
247}
248
249#[must_use] pub fn resolve_pixel_value_no_percent_with_viewport(
252 pv: &PixelValue,
253 em_size: f32,
254 rem_size: f32,
255 viewport_width: f32,
256 viewport_height: f32,
257) -> Option<f32> {
258 match pv.metric {
259 SizeMetric::Vw => Some(pv.number.get() / 100.0 * viewport_width),
260 SizeMetric::Vh => Some(pv.number.get() / 100.0 * viewport_height),
261 SizeMetric::Vmin => Some(pv.number.get() / 100.0 * viewport_width.min(viewport_height)),
262 SizeMetric::Vmax => Some(pv.number.get() / 100.0 * viewport_width.max(viewport_height)),
263 _ => resolve_pixel_value_no_percent(pv, em_size, rem_size),
264 }
265}
266