azul_layout/solver3/
calc.rs1use azul_css::props::{
11 basic::{
12 pixel::PT_TO_PX,
13 PixelValue, SizeMetric,
14 },
15 layout::dimensions::{CalcAstItem, CalcAstItemVec},
16};
17
18const PX_PER_INCH: f32 = 96.0;
20const CM_PER_INCH: f32 = 2.54;
22const 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
41#[derive(Clone, Debug)]
43enum CalcFlatItem {
44 Num(f32),
45 Op(CalcOp),
46}
47
48#[derive(Clone, Copy, Debug, PartialEq)]
50enum CalcOp {
51 Add,
52 Sub,
53 Mul,
54 Div,
55}
56
57pub fn evaluate_calc(ctx: &CalcResolveContext, basis: f32) -> f32 {
60 evaluate_calc_ast(ctx.items.as_slice(), basis, ctx.em_size, ctx.rem_size)
61}
62
63fn evaluate_calc_ast(
74 items: &[CalcAstItem],
75 basis: f32,
76 em_size: f32,
77 rem_size: f32,
78) -> f32 {
79 let mut flat: Vec<CalcFlatItem> = Vec::with_capacity(items.len());
81 let mut i = 0;
82 while i < items.len() {
83 match &items[i] {
84 CalcAstItem::Value(pv) => {
85 flat.push(CalcFlatItem::Num(resolve_pixel_value(
86 pv, basis, em_size, rem_size,
87 )));
88 }
89 CalcAstItem::Add => flat.push(CalcFlatItem::Op(CalcOp::Add)),
90 CalcAstItem::Sub => flat.push(CalcFlatItem::Op(CalcOp::Sub)),
91 CalcAstItem::Mul => flat.push(CalcFlatItem::Op(CalcOp::Mul)),
92 CalcAstItem::Div => flat.push(CalcFlatItem::Op(CalcOp::Div)),
93 CalcAstItem::BraceOpen => {
94 let start = i + 1;
96 let mut depth = 1u32;
97 let mut j = start;
98 while j < items.len() && depth > 0 {
99 match &items[j] {
100 CalcAstItem::BraceOpen => depth += 1,
101 CalcAstItem::BraceClose => depth -= 1,
102 _ => {}
103 }
104 if depth > 0 {
105 j += 1;
106 }
107 }
108 let sub_val = evaluate_calc_ast(&items[start..j], basis, em_size, rem_size);
110 flat.push(CalcFlatItem::Num(sub_val));
111 i = j; }
113 CalcAstItem::BraceClose => { }
114 }
115 i += 1;
116 }
117
118 let mut pass2: Vec<CalcFlatItem> = Vec::with_capacity(flat.len());
120 let mut k = 0;
121 while k < flat.len() {
122 if let CalcFlatItem::Op(op @ (CalcOp::Mul | CalcOp::Div)) = &flat[k] {
123 if let (Some(CalcFlatItem::Num(lhs)), Some(CalcFlatItem::Num(rhs))) =
125 (pass2.last(), flat.get(k + 1))
126 {
127 let result = match op {
128 CalcOp::Mul => lhs * rhs,
129 CalcOp::Div => {
130 if *rhs != 0.0 {
131 lhs / rhs
132 } else {
133 0.0
134 }
135 }
136 _ => unreachable!(),
137 };
138 *pass2.last_mut().unwrap() = CalcFlatItem::Num(result);
139 k += 2; continue;
141 }
142 }
143 pass2.push(flat[k].clone());
144 k += 1;
145 }
146
147 let mut result = match pass2.first() {
149 Some(CalcFlatItem::Num(v)) => *v,
150 _ => return 0.0,
151 };
152 let mut m = 1;
153 while m < pass2.len() {
154 if let (CalcFlatItem::Op(op), Some(CalcFlatItem::Num(rhs))) =
155 (&pass2[m], pass2.get(m + 1))
156 {
157 match op {
158 CalcOp::Add => result += rhs,
159 CalcOp::Sub => result -= rhs,
160 _ => {} }
162 m += 2;
163 } else {
164 m += 1;
165 }
166 }
167
168 result
169}
170
171pub fn resolve_pixel_value(
177 pv: &PixelValue,
178 basis: f32,
179 em_size: f32,
180 rem_size: f32,
181) -> f32 {
182 match pv.metric {
183 SizeMetric::Px => pv.number.get(),
184 SizeMetric::Pt => pv.number.get() * PT_TO_PX,
185 SizeMetric::In => pv.number.get() * PX_PER_INCH,
186 SizeMetric::Cm => pv.number.get() * PX_PER_INCH / CM_PER_INCH,
187 SizeMetric::Mm => pv.number.get() * PX_PER_INCH / MM_PER_INCH,
188 SizeMetric::Em => pv.number.get() * em_size,
189 SizeMetric::Rem => pv.number.get() * rem_size,
190 SizeMetric::Percent => basis * (pv.number.get() / 100.0),
191 SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => {
192 pv.number.get()
194 }
195 }
196}
197
198pub fn resolve_pixel_value_with_viewport(
200 pv: &PixelValue,
201 basis: f32,
202 em_size: f32,
203 rem_size: f32,
204 viewport_width: f32,
205 viewport_height: f32,
206) -> f32 {
207 match pv.metric {
208 SizeMetric::Vw => pv.number.get() / 100.0 * viewport_width,
209 SizeMetric::Vh => pv.number.get() / 100.0 * viewport_height,
210 SizeMetric::Vmin => pv.number.get() / 100.0 * viewport_width.min(viewport_height),
211 SizeMetric::Vmax => pv.number.get() / 100.0 * viewport_width.max(viewport_height),
212 _ => resolve_pixel_value(pv, basis, em_size, rem_size),
213 }
214}
215