Skip to main content

pfx_live/flat/
mod.rs

1#[cfg(test)]
2mod corner_tests;
3pub mod cpu;
4pub mod cpu_text;
5#[cfg(test)]
6mod cpu_text_tests;
7pub mod effects;
8pub mod grid;
9pub mod icons;
10pub mod look;
11mod pass;
12mod roles;
13pub mod sdf;
14pub mod shader;
15pub mod shadow;
16#[cfg(test)]
17mod sprite_tests;
18pub mod sprites;
19pub mod style;
20#[cfg(test)]
21mod style_tests;
22pub mod svg;
23#[cfg(test)]
24mod tests;
25pub mod viewport;
26
27use crate::text::{GpuAtlas, RichQuad};
28use bytemuck::{Pod, Zeroable};
29pub use icons::{Icon, IconKind, IconShape, Icons};
30pub use pass::{
31    Environment, FlatIcons, FlatPass, FlatTarget, finish_entries, layer_entries, text_entries,
32    view_entries,
33};
34use pfx_gpu::window::{Letterbox, Size, letterbox_nearest};
35pub use pfx_post::look::{Gradient, GradientShape, Interpolation};
36use pfx_text::Paragraph;
37pub use roles::{Roles, TokenKey};
38pub use shadow::{CurvePoint, ShadowCurve};
39pub use sprites::{FlatSprites, SpriteFilter, SpriteImage};
40pub use style::{Bevel, Outline, Style};
41
42pub type Matrix = [[f32; 4]; 4];
43
44pub const IDENTITY: Matrix = [
45    [1.0, 0.0, 0.0, 0.0],
46    [0.0, 1.0, 0.0, 0.0],
47    [0.0, 0.0, 1.0, 0.0],
48    [0.0, 0.0, 0.0, 1.0],
49];
50
51#[derive(Clone, Copy, Debug, PartialEq)]
52pub struct Srgba(pub [f32; 4]);
53
54impl Srgba {
55    pub const TRANSPARENT: Self = Self([0.0; 4]);
56    pub const WHITE: Self = Self([1.0; 4]);
57
58    pub const fn hex(rgb: u32) -> Self {
59        Self([
60            ((rgb >> 16) & 255) as f32 / 255.0,
61            ((rgb >> 8) & 255) as f32 / 255.0,
62            (rgb & 255) as f32 / 255.0,
63            1.0,
64        ])
65    }
66
67    pub const fn alpha(self, alpha: f32) -> Self {
68        Self([self.0[0], self.0[1], self.0[2], alpha])
69    }
70
71    pub fn premultiplied(self) -> [f32; 4] {
72        let [r, g, b, a] = self.0;
73        [r * a, g * a, b * a, a]
74    }
75}
76
77#[derive(Clone, Copy, Debug, PartialEq)]
78pub struct Fit {
79    pub layout: [f32; 2],
80    pub target: [u32; 2],
81    pub scale: f32,
82    pub offset: [f32; 2],
83    pub letterbox: Letterbox,
84}
85
86impl Fit {
87    pub fn new(layout: [f32; 2], target: [u32; 2]) -> Result<Self, String> {
88        if !(layout[0] > 0.0 && layout[1] > 0.0 && layout[0].is_finite() && layout[1].is_finite()) {
89            return Err("the layout size must be finite and positive".into());
90        }
91        let letterbox = letterbox_nearest(
92            layout[0],
93            layout[1],
94            Size {
95                width: target[0],
96                height: target[1],
97            },
98        )
99        .ok_or("the target is empty")?;
100        Ok(Self {
101            layout,
102            target,
103            scale: letterbox.matrix[0],
104            offset: [letterbox.matrix[12], letterbox.matrix[13]],
105            letterbox,
106        })
107    }
108
109    pub fn to_target(&self, point: [f32; 2]) -> [f32; 2] {
110        self.letterbox.map(point[0], point[1])
111    }
112
113    pub fn to_layout(&self, pixel: [f32; 2]) -> [f32; 2] {
114        self.letterbox.unmap(pixel[0], pixel[1])
115    }
116
117    pub fn viewport(&self) -> [f32; 4] {
118        [
119            self.offset[0],
120            self.offset[1],
121            self.offset[0] + self.layout[0] * self.scale,
122            self.offset[1] + self.layout[1] * self.scale,
123        ]
124    }
125
126    pub fn clip_from_layout(&self) -> Matrix {
127        let sx = 2.0 * self.scale / self.target[0] as f32;
128        let sy = -2.0 * self.scale / self.target[1] as f32;
129        [
130            [sx, 0.0, 0.0, 0.0],
131            [0.0, sy, 0.0, 0.0],
132            [0.0, 0.0, 0.0, 0.0],
133            [
134                2.0 * self.offset[0] / self.target[0] as f32 - 1.0,
135                1.0 - 2.0 * self.offset[1] / self.target[1] as f32,
136                0.5,
137                1.0,
138            ],
139        ]
140    }
141}
142
143pub fn multiply(a: Matrix, b: Matrix) -> Matrix {
144    crate::frame::multiply(a, b)
145}
146
147pub fn translation(x: f32, y: f32) -> Matrix {
148    let mut m = IDENTITY;
149    m[3][0] = x;
150    m[3][1] = y;
151    m
152}
153
154pub fn rotation(radians: f32) -> Matrix {
155    let (s, c) = radians.sin_cos();
156    let mut m = IDENTITY;
157    m[0][0] = c;
158    m[0][1] = s;
159    m[1][0] = -s;
160    m[1][1] = c;
161    m
162}
163
164pub fn scaling(x: f32, y: f32) -> Matrix {
165    let mut m = IDENTITY;
166    m[0][0] = x;
167    m[1][1] = y;
168    m[2][2] = x.abs().max(y.abs());
169    m
170}
171
172pub fn tip_x(radians: f32) -> Matrix {
173    let (s, c) = radians.sin_cos();
174    let mut m = IDENTITY;
175    m[1][1] = c;
176    m[1][2] = s;
177    m[2][1] = -s;
178    m[2][2] = c;
179    m
180}
181
182pub fn tip_y(radians: f32) -> Matrix {
183    let (s, c) = radians.sin_cos();
184    let mut m = IDENTITY;
185    m[0][0] = c;
186    m[0][2] = -s;
187    m[2][0] = s;
188    m[2][2] = c;
189    m
190}
191
192pub fn place(centre: [f32; 2], radians: f32) -> Matrix {
193    multiply(translation(centre[0], centre[1]), rotation(radians))
194}
195
196pub fn clamp_radii(half: [f32; 2], radii: [f32; 4]) -> [f32; 4] {
197    let limit = half[0].min(half[1]);
198    radii.map(|radius| radius.min(limit).max(0.0))
199}
200
201#[derive(Clone, Copy, Debug, PartialEq)]
202pub enum Shape {
203    Rect {
204        half: [f32; 2],
205        radii: [f32; 4],
206    },
207    Circle {
208        radius: f32,
209    },
210    Ring {
211        radius: f32,
212        width: f32,
213    },
214    Arc {
215        radius: f32,
216        width: f32,
217        start: f32,
218        sweep: f32,
219    },
220    Icon(Icon),
221    Text(usize),
222}
223
224#[derive(Clone, Copy, Debug, PartialEq)]
225pub enum Fill {
226    None,
227    Solid(Srgba),
228    Vertical(Srgba, Srgba),
229    Gradient(Gradient),
230    Image(SpriteFill),
231}
232
233#[derive(Clone, Copy, Debug, PartialEq)]
234pub struct SpriteFill {
235    pub frame: [f32; 4],
236    pub tint: Srgba,
237}
238
239impl SpriteFill {
240    pub fn new(frame: [f32; 4]) -> Self {
241        Self {
242            frame,
243            tint: Srgba::WHITE,
244        }
245    }
246}
247
248#[derive(Clone, Copy, Debug, PartialEq)]
249pub struct Dash {
250    pub on: f32,
251    pub off: f32,
252    pub phase: f32,
253}
254
255#[derive(Clone, Copy, Debug, PartialEq)]
256pub struct Stroke {
257    pub colour: Srgba,
258    pub width: f32,
259    pub dash: Option<Dash>,
260}
261
262impl Stroke {
263    pub fn solid(colour: Srgba, width: f32) -> Self {
264        Self {
265            colour,
266            width,
267            dash: None,
268        }
269    }
270
271    pub fn dashed(colour: Srgba, width: f32, on: f32, off: f32) -> Self {
272        Self {
273            colour,
274            width,
275            dash: Some(Dash {
276                on,
277                off,
278                phase: 0.0,
279            }),
280        }
281    }
282}
283
284#[derive(Clone, Copy, Debug, PartialEq)]
285pub struct Glow {
286    pub colour: Srgba,
287    pub sigma: f32,
288}
289
290#[derive(Clone, Copy, Debug, Default, PartialEq)]
291pub enum Shadow {
292    None,
293    #[default]
294    Cast,
295    Turned,
296    Glow(Glow),
297}
298
299#[derive(Clone, Copy, Debug, Default, PartialEq)]
300pub struct Material {
301    pub thickness: f32,
302    pub bevel: f32,
303    pub gloss: f32,
304    pub roughness: f32,
305    pub reflection: f32,
306}
307
308impl Material {
309    pub fn is_flat(&self) -> bool {
310        self.thickness <= 0.0 && self.bevel <= 0.0 && self.gloss <= 0.0 && self.reflection <= 0.0
311    }
312}
313
314#[derive(Clone, Copy, Debug, PartialEq)]
315pub struct Draw {
316    pub transform: Matrix,
317    pub elevation: f32,
318    pub shape: Shape,
319    pub fill: Fill,
320    pub stroke: Option<Stroke>,
321    pub opacity: f32,
322    pub shadow: Shadow,
323    pub material: Material,
324    pub pattern: Option<Pattern>,
325    pub id: u32,
326    pub group: Option<u16>,
327    pub style: Option<Style>,
328    pub fx: Fx,
329    pub roles: Roles,
330}
331
332#[derive(Clone, Copy, Debug, Default, PartialEq)]
333pub struct Fx {
334    pub blur: [f32; 2],
335    pub additive: bool,
336    pub unpicked: bool,
337}
338
339impl Fx {
340    pub fn is_none(&self) -> bool {
341        *self == Self::default()
342    }
343}
344
345#[derive(Clone, Copy, Debug, PartialEq, Eq)]
346pub enum PatternKind {
347    Stripes,
348    Dots,
349    CrossHatch,
350    Chevrons,
351}
352
353#[derive(Clone, Copy, Debug, PartialEq)]
354pub struct Pattern {
355    pub kind: PatternKind,
356    pub scale: f32,
357    pub angle: f32,
358    pub width: f32,
359    pub colour: Srgba,
360}
361
362impl Pattern {
363    pub fn new(kind: PatternKind, scale: f32, angle: f32, width: f32, colour: Srgba) -> Self {
364        Self {
365            kind,
366            scale,
367            angle,
368            width,
369            colour,
370        }
371    }
372
373    fn bits(&self) -> u32 {
374        let code = match self.kind {
375            PatternKind::Stripes => 1,
376            PatternKind::Dots => 2,
377            PatternKind::CrossHatch => 3,
378            PatternKind::Chevrons => 4,
379        };
380        code << PATTERN_SHIFT
381    }
382
383    fn packed_colour(&self) -> u32 {
384        let byte = |value: f32| (value.clamp(0.0, 1.0) * 255.0).round() as u32;
385        let [r, g, b, a] = self.colour.0;
386        byte(r) | byte(g) << 8 | byte(b) << 16 | byte(a) << 24
387    }
388}
389
390impl Shape {
391    pub fn rounded(half: [f32; 2], radius: f32) -> Self {
392        Self::Rect {
393            half,
394            radii: [radius; 4],
395        }
396    }
397
398    pub fn corners(half: [f32; 2], radii: [f32; 4]) -> Self {
399        Self::Rect { half, radii }
400    }
401}
402
403impl Draw {
404    pub fn new(shape: Shape) -> Self {
405        Self {
406            transform: IDENTITY,
407            elevation: 0.0,
408            shape,
409            fill: Fill::None,
410            stroke: None,
411            opacity: 1.0,
412            shadow: Shadow::Cast,
413            material: Material::default(),
414            pattern: None,
415            id: 0,
416            group: None,
417            style: None,
418            fx: Fx::default(),
419            roles: Roles::default(),
420        }
421    }
422
423    pub fn rect(centre: [f32; 2], size: [f32; 2], radius: f32) -> Self {
424        Self::rect_corners(centre, size, [radius; 4])
425    }
426
427    pub fn rect_corners(centre: [f32; 2], size: [f32; 2], radii: [f32; 4]) -> Self {
428        Self::new(Shape::Rect {
429            half: [size[0] * 0.5, size[1] * 0.5],
430            radii,
431        })
432        .at(centre)
433    }
434
435    pub fn sprite(centre: [f32; 2], size: [f32; 2], frame: [f32; 4]) -> Self {
436        let mut draw = Self::rect(centre, size, 0.0);
437        draw.fill = Fill::Image(SpriteFill::new(frame));
438        draw
439    }
440
441    pub fn circle(centre: [f32; 2], radius: f32) -> Self {
442        Self::new(Shape::Circle { radius }).at(centre)
443    }
444
445    pub fn at(mut self, centre: [f32; 2]) -> Self {
446        self.transform = translation(centre[0], centre[1]);
447        self
448    }
449
450    pub fn transform(mut self, transform: Matrix) -> Self {
451        self.transform = transform;
452        self
453    }
454
455    pub fn elevation(mut self, elevation: f32) -> Self {
456        self.elevation = elevation;
457        self
458    }
459
460    pub fn fill(mut self, colour: Srgba) -> Self {
461        self.fill = Fill::Solid(colour);
462        self
463    }
464
465    pub fn vertical(mut self, top: Srgba, bottom: Srgba) -> Self {
466        self.fill = Fill::Vertical(top, bottom);
467        self
468    }
469
470    pub fn stroke(mut self, stroke: Stroke) -> Self {
471        self.stroke = Some(stroke);
472        self
473    }
474
475    pub fn opacity(mut self, opacity: f32) -> Self {
476        self.opacity = opacity;
477        self
478    }
479
480    pub fn shadow(mut self, shadow: Shadow) -> Self {
481        self.shadow = shadow;
482        self
483    }
484
485    pub fn material(mut self, material: Material) -> Self {
486        self.material = material;
487        self
488    }
489
490    pub fn pattern(mut self, pattern: Pattern) -> Self {
491        self.pattern = Some(pattern);
492        self
493    }
494
495    pub fn id(mut self, id: u32) -> Self {
496        self.id = id;
497        self
498    }
499
500    pub fn group(mut self, group: u16) -> Self {
501        self.group = Some(group);
502        self
503    }
504
505    pub fn style(mut self, style: Style) -> Self {
506        self.style = Some(style);
507        self
508    }
509
510    pub fn fill_token(mut self, key: impl Into<TokenKey>) -> Self {
511        self.roles.fill = Some(key.into());
512        self
513    }
514
515    pub fn fill_end_token(mut self, key: impl Into<TokenKey>) -> Self {
516        self.roles.fill_end = Some(key.into());
517        self
518    }
519
520    pub fn stroke_token(mut self, key: impl Into<TokenKey>) -> Self {
521        self.roles.stroke = Some(key.into());
522        self
523    }
524
525    pub fn pattern_token(mut self, key: impl Into<TokenKey>) -> Self {
526        self.roles.pattern = Some(key.into());
527        self
528    }
529
530    pub fn glow_token(mut self, key: impl Into<TokenKey>) -> Self {
531        self.roles.glow = Some(key.into());
532        self
533    }
534
535    pub fn blur(mut self, displacement: [f32; 2]) -> Self {
536        self.fx.blur = displacement;
537        self
538    }
539
540    pub fn additive(mut self) -> Self {
541        self.fx.additive = true;
542        self
543    }
544
545    pub fn unpicked(mut self) -> Self {
546        self.fx.unpicked = true;
547        self
548    }
549}
550
551#[derive(Clone, Copy, Debug, PartialEq)]
552pub struct Group {
553    pub opacity: f32,
554}
555
556#[derive(Clone, Copy, Debug, PartialEq)]
557pub struct Light {
558    pub shadow: [f32; 2],
559    pub key: [f32; 3],
560    pub ambient: f32,
561}
562
563impl Default for Light {
564    fn default() -> Self {
565        Self {
566            shadow: [0.0, 1.0],
567            key: normalize3([0.0, -0.5, 1.0]),
568            ambient: 0.55,
569        }
570    }
571}
572
573#[derive(Clone, Copy)]
574pub enum Glyphs<'a> {
575    Paragraph(&'a Paragraph),
576    Quads(&'a [RichQuad]),
577}
578
579#[derive(Clone, Copy)]
580pub struct FlatText<'a> {
581    pub atlas: &'a GpuAtlas,
582    pub glyphs: Glyphs<'a>,
583    pub colour: Srgba,
584}
585
586#[derive(Clone, Copy)]
587pub struct FlatScene<'a> {
588    pub layout: [f32; 2],
589    pub clear: Option<Srgba>,
590    pub curve: &'a ShadowCurve,
591    pub light: Light,
592    pub draws: &'a [Draw],
593    pub groups: &'a [Group],
594    pub text: &'a [FlatText<'a>],
595    pub icons: Option<&'a FlatIcons>,
596    pub sprites: Option<&'a FlatSprites>,
597    pub environment: Option<&'a Environment>,
598    pub post: bool,
599    pub frame: u32,
600    pub seed: u32,
601}
602
603pub(crate) fn normalize3(v: [f32; 3]) -> [f32; 3] {
604    let l = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
605    if l > 0.0 {
606        [v[0] / l, v[1] / l, v[2] / l]
607    } else {
608        [0.0, 0.0, 1.0]
609    }
610}
611
612pub const KIND_RECT: u32 = 0;
613pub const KIND_CIRCLE: u32 = 1;
614pub const KIND_RING: u32 = 2;
615pub const KIND_ARC: u32 = 3;
616pub const KIND_ICON_FILL: u32 = 4;
617pub const KIND_ICON_LINE: u32 = 5;
618pub const KIND_SHADOW: u32 = 6;
619pub const FILL_SOLID: u32 = 1 << 4;
620pub const FILL_VERTICAL: u32 = 2 << 4;
621pub const FILL_GRADIENT: u32 = 3 << 4;
622pub const FILL_IMAGE: u32 = 4 << 4;
623pub const FILL_MASK: u32 = 15 << 4;
624pub const DASHED: u32 = 1 << 8;
625pub const SLAB: u32 = 1 << 9;
626pub const LIT: u32 = 1 << 10;
627pub const BLUR: u32 = 1 << 11;
628pub const PATTERN_SHIFT: u32 = 12;
629pub const PATTERN_MASK: u32 = 7 << PATTERN_SHIFT;
630pub const ADDITIVE: u32 = 1 << 15;
631pub const UNPICKED: u32 = 1 << 16;
632pub const FX: u32 = BLUR | ADDITIVE | UNPICKED;
633pub const BLUR_MIN_PIXELS: f32 = 0.25;
634
635#[repr(C)]
636#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
637pub struct Instance {
638    pub axes: [f32; 4],
639    pub origin: [f32; 4],
640    pub bounds: [f32; 4],
641    pub shape: [f32; 4],
642    pub stroke: [f32; 4],
643    pub fill_top: [f32; 4],
644    pub fill_bottom: [f32; 4],
645    pub stroke_colour: [f32; 4],
646    pub icon: [f32; 4],
647    pub info: [u32; 4],
648    pub material: [f32; 4],
649    pub frame: [[f32; 4]; 3],
650    pub corners: [f32; 4],
651}
652
653pub const INSTANCE_ATTRIBUTES: u32 = 15;
654
655impl Instance {
656    pub fn kind(&self) -> u32 {
657        self.info[0] & 15
658    }
659
660    pub fn opacity(&self) -> f32 {
661        f32::from_bits(self.info[3])
662    }
663
664    pub fn inverse(&self) -> [[f32; 2]; 2] {
665        let [a, b, c, d] = self.axes;
666        let det = a * d - c * b;
667        [[d / det, -b / det], [-c / det, a / det]]
668    }
669
670    pub fn margin(&self) -> [f32; 2] {
671        let inverse = self.inverse();
672        [
673            1.5 * (inverse[0][0] * inverse[0][0] + inverse[1][0] * inverse[1][0]).sqrt(),
674            1.5 * (inverse[0][1] * inverse[0][1] + inverse[1][1] * inverse[1][1]).sqrt(),
675        ]
676    }
677
678    pub fn pixel_bounds(&self) -> [f32; 4] {
679        let margin = self.margin();
680        let [cx, cy, hx, hy] = self.bounds;
681        let mut out = [f32::MAX, f32::MAX, f32::MIN, f32::MIN];
682        for corner in [[-1.0, -1.0], [1.0, -1.0], [-1.0, 1.0], [1.0, 1.0]] {
683            let local = [
684                cx + corner[0] * (hx + margin[0]),
685                cy + corner[1] * (hy + margin[1]),
686            ];
687            let pixel = [
688                self.axes[0] * local[0] + self.axes[2] * local[1] + self.origin[0],
689                self.axes[1] * local[0] + self.axes[3] * local[1] + self.origin[1],
690            ];
691            out = [
692                out[0].min(pixel[0]),
693                out[1].min(pixel[1]),
694                out[2].max(pixel[0]),
695                out[3].max(pixel[1]),
696            ];
697        }
698        out
699    }
700
701    pub fn to_local(&self, pixel: [f32; 2]) -> [f32; 2] {
702        let inverse = self.inverse();
703        let d = [pixel[0] - self.origin[0], pixel[1] - self.origin[1]];
704        [
705            inverse[0][0] * d[0] + inverse[1][0] * d[1],
706            inverse[0][1] * d[0] + inverse[1][1] * d[1],
707        ]
708    }
709}
710
711pub(crate) fn positive(value: f32) -> bool {
712    value > 0.0
713}
714
715fn length2(v: [f32; 2]) -> f32 {
716    (v[0] * v[0] + v[1] * v[1]).sqrt()
717}
718
719#[derive(Clone, Copy, Debug)]
720pub(crate) struct Placed {
721    pub axes: [f32; 4],
722    pub origin: [f32; 2],
723    pub layout_axes: [f32; 4],
724}
725
726pub(crate) fn placed(transform: &Matrix, fit: &Fit) -> Option<Placed> {
727    let layout_axes = [
728        transform[0][0],
729        transform[0][1],
730        transform[1][0],
731        transform[1][1],
732    ];
733    let det = layout_axes[0] * layout_axes[3] - layout_axes[2] * layout_axes[1];
734    if !det.is_finite() || det.abs() < 1e-8 {
735        return None;
736    }
737    if transform.iter().flatten().any(|value| !value.is_finite()) {
738        return None;
739    }
740    Some(Placed {
741        axes: layout_axes.map(|value| value * fit.scale),
742        origin: fit.to_target([transform[3][0], transform[3][1]]),
743        layout_axes,
744    })
745}
746
747fn rotation_rows(transform: &Matrix) -> [[f32; 4]; 3] {
748    let columns =
749        [0, 1, 2].map(|c| normalize3([transform[c][0], transform[c][1], transform[c][2]]));
750    [0, 1, 2].map(|r| [columns[0][r], columns[1][r], columns[2][r], 0.0])
751}
752
753pub(crate) fn caster(draw: &Draw) -> Option<([f32; 2], [f32; 4])> {
754    let grow = draw
755        .stroke
756        .map_or(0.0, |stroke| stroke.width.max(0.0) * 0.5);
757    match draw.shape {
758        Shape::Rect { half, radii } => Some((
759            [half[0] + grow, half[1] + grow],
760            clamp_radii(half, radii).map(|radius| radius + grow),
761        )),
762        Shape::Circle { radius } => Some(([radius + grow; 2], [radius + grow; 4])),
763        Shape::Ring { radius, width } | Shape::Arc { radius, width, .. } => {
764            let outer = radius + width * 0.5 + grow;
765            Some(([outer; 2], [outer; 4]))
766        }
767        Shape::Icon(_) | Shape::Text(_) => None,
768    }
769}
770
771fn paint_alpha(draw: &Draw) -> f32 {
772    let fill = match draw.fill {
773        Fill::None => 0.0,
774        Fill::Solid(colour) => colour.0[3],
775        Fill::Vertical(top, bottom) => top.0[3].max(bottom.0[3]),
776        Fill::Gradient(gradient) => gradient.max_alpha(),
777        Fill::Image(image) => image.tint.0[3],
778    };
779    let stroke = draw
780        .stroke
781        .filter(|stroke| stroke.width > 0.0)
782        .map_or(0.0, |stroke| stroke.colour.0[3]);
783    fill.max(stroke).clamp(0.0, 1.0)
784}
785
786pub fn slab_size(draw: &Draw, icons: Option<&Icons>) -> Option<(f32, f32)> {
787    let material = draw.material;
788    if !(positive(material.thickness) || positive(material.bevel)) {
789        return None;
790    }
791    let m = draw.transform;
792    let scale = (m[0][0] * m[0][0] + m[0][1] * m[0][1] + m[0][2] * m[0][2]).sqrt();
793    if !positive(scale) || !scale.is_finite() {
794        return None;
795    }
796    let mut thickness = material.thickness.max(material.bevel).max(0.0);
797    let bevel_limit = match draw.shape {
798        Shape::Rect { half, .. } => half[0].min(half[1]) * scale,
799        Shape::Circle { radius } => radius * scale,
800        Shape::Icon(handle) => {
801            let entry = icons?.entry(handle)?;
802            let side = (entry.bounds[2] - entry.bounds[0]).min(entry.bounds[3] - entry.bounds[1]);
803            thickness = thickness.min(side * scale / 8.0);
804            match entry.kind {
805                IconKind::Line => draw.stroke.map_or(0.0, |stroke| stroke.width * scale * 0.5),
806                IconKind::Fill => f32::MAX,
807            }
808        }
809        _ => return None,
810    };
811    let bevel = material
812        .bevel
813        .clamp(0.0, (thickness * 0.5).min(bevel_limit));
814    Some((thickness / scale, bevel / scale))
815}
816
817pub fn pack_shape(draw: &Draw, fit: &Fit, icons: Option<&Icons>) -> Option<Instance> {
818    match (draw.style, draw.fill) {
819        (None, _) => pack_plain(draw, fit, icons),
820        (Some(style), Fill::Image(image)) => {
821            let tinted = Draw {
822                fill: Fill::Solid(image.tint),
823                ..*draw
824            };
825            style::pack(&tinted, style, fit, icons)
826        }
827        (Some(style), _) => style::pack(draw, style, fit, icons),
828    }
829}
830
831pub(crate) fn pack_plain(draw: &Draw, fit: &Fit, icons: Option<&Icons>) -> Option<Instance> {
832    let placed = placed(&draw.transform, fit)?;
833    if !positive(draw.opacity)
834        || (draw.fill == Fill::None && draw.stroke.is_none() && draw.pattern.is_none())
835    {
836        return None;
837    }
838    let stroke = draw
839        .stroke
840        .filter(|stroke| stroke.width > 0.0 && stroke.width.is_finite());
841    let grow = stroke.map_or(0.0, |stroke| stroke.width * 0.5);
842    let mut kind;
843    let mut shape = [0.0; 4];
844    let mut corners = [0.0; 4];
845    let mut icon = [0.0; 4];
846    let bounds;
847    let gradient;
848    match draw.shape {
849        Shape::Rect { half, radii } => {
850            kind = KIND_RECT;
851            corners = clamp_radii(half, radii);
852            shape = [half[0], half[1], 0.0, 0.0];
853            bounds = [0.0, 0.0, half[0] + grow, half[1] + grow];
854            gradient = [-half[1], half[1]];
855        }
856        Shape::Circle { radius } => {
857            kind = KIND_CIRCLE;
858            shape[0] = radius;
859            bounds = [0.0, 0.0, radius + grow, radius + grow];
860            gradient = [-radius, radius];
861        }
862        Shape::Ring { radius, width } => {
863            kind = KIND_RING;
864            shape = [radius, width, 0.0, 0.0];
865            let outer = radius + width * 0.5 + grow;
866            bounds = [0.0, 0.0, outer, outer];
867            gradient = [-outer, outer];
868        }
869        Shape::Arc {
870            radius,
871            width,
872            start,
873            sweep,
874        } => {
875            kind = KIND_ARC;
876            shape = [radius, width, start, sweep];
877            let outer = radius + width * 0.5 + grow;
878            bounds = [0.0, 0.0, outer, outer];
879            gradient = [-outer, outer];
880        }
881        Shape::Icon(handle) => {
882            let entry = icons?.entry(handle)?;
883            kind = match entry.kind {
884                IconKind::Fill => KIND_ICON_FILL,
885                IconKind::Line => KIND_ICON_LINE,
886            };
887            let [x0, y0, x1, y1] = entry.bounds;
888            bounds = [
889                (x0 + x1) * 0.5,
890                (y0 + y1) * 0.5,
891                (x1 - x0) * 0.5,
892                (y1 - y0) * 0.5,
893            ];
894            icon = entry.uv;
895            gradient = [y0, y1];
896        }
897        Shape::Text(_) => return None,
898    }
899    let mut flags = 0;
900    let (top, bottom) = match draw.fill {
901        Fill::None => (Srgba::TRANSPARENT, Srgba::TRANSPARENT),
902        Fill::Solid(colour) => {
903            flags |= FILL_SOLID;
904            (colour, colour)
905        }
906        Fill::Vertical(top, bottom) => {
907            flags |= FILL_VERTICAL;
908            (top, bottom)
909        }
910        Fill::Image(image) => {
911            flags |= FILL_IMAGE;
912            let reach = match draw.shape {
913                Shape::Rect { half, .. } => Some(half),
914                Shape::Circle { radius } => Some([radius; 2]),
915                Shape::Ring { radius, width } | Shape::Arc { radius, width, .. } => {
916                    Some([radius + width * 0.5; 2])
917                }
918                Shape::Icon(_) | Shape::Text(_) => None,
919            };
920            match reach.filter(|half| positive(half[0]) && positive(half[1])) {
921                Some(half) => {
922                    let size = [
923                        image.frame[2] - image.frame[0],
924                        image.frame[3] - image.frame[1],
925                    ];
926                    icon = [
927                        image.frame[0] + size[0] * 0.5,
928                        image.frame[1] + size[1] * 0.5,
929                        size[0] / (2.0 * half[0]),
930                        size[1] / (2.0 * half[1]),
931                    ];
932                }
933                None => flags = flags & !FILL_IMAGE | FILL_SOLID,
934            }
935            (image.tint, image.tint)
936        }
937        Fill::Gradient(gradient) => {
938            flags |= FILL_GRADIENT;
939            let header = gradient.header();
940            (
941                Srgba(gradient.geometry()),
942                Srgba([0.0, header[0], header[1] + 2.0 * header[2], header[3]]),
943            )
944        }
945    };
946    if kind == KIND_ICON_LINE {
947        flags &= !FILL_MASK;
948    }
949    let mut pattern_colour = 0;
950    if let Some(pattern) = draw.pattern
951        && matches!(kind, KIND_RECT | KIND_CIRCLE | KIND_RING | KIND_ARC)
952        && flags & FILL_MASK != FILL_IMAGE
953        && positive(pattern.scale)
954        && pattern.scale.is_finite()
955        && pattern.angle.is_finite()
956        && positive(pattern.colour.0[3])
957    {
958        flags |= pattern.bits();
959        if flags & (FILL_SOLID | FILL_VERTICAL) == 0 {
960            flags |= FILL_SOLID;
961        }
962        icon = [
963            pattern.scale,
964            pattern.angle,
965            pattern.width.clamp(0.0, 1.0),
966            0.0,
967        ];
968        pattern_colour = pattern.packed_colour();
969    }
970    let mut stroke_params = [0.0; 4];
971    let mut stroke_colour = [0.0; 4];
972    if let Some(stroke) = stroke {
973        stroke_params[0] = stroke.width;
974        stroke_colour = stroke.colour.0;
975        if let Some(dash) = stroke.dash
976            && dash.on > 0.0
977            && dash.off > 0.0
978            && matches!(kind, KIND_RECT | KIND_CIRCLE)
979        {
980            flags |= DASHED;
981            stroke_params[1] = dash.on;
982            stroke_params[2] = dash.off;
983            stroke_params[3] = dash.phase;
984        }
985    }
986    let material = draw.material;
987    let mut material_params = [0.0; 4];
988    let mut frame = [[0.0; 4]; 3];
989    let flat_bounds = bounds;
990    let mut bounds = bounds;
991    if !material.is_flat() {
992        flags |= LIT;
993        frame = rotation_rows(&draw.transform);
994        frame[0][3] = material.reflection.max(0.0);
995        frame[1][3] = material.roughness.clamp(0.02, 1.0);
996        material_params[2] = material.gloss.max(0.0);
997        if let Some((thickness, bevel)) = slab_size(draw, icons) {
998            flags |= SLAB;
999            material_params[0] = thickness;
1000            material_params[1] = bevel;
1001            let down = [frame[2][0], frame[2][1], frame[2][2]];
1002            if down[2].abs() > 1e-3 {
1003                let reach = [
1004                    (thickness * down[0] / down[2]).abs(),
1005                    (thickness * down[1] / down[2]).abs(),
1006                ];
1007                bounds[2] += reach[0];
1008                bounds[3] += reach[1];
1009            }
1010        }
1011    }
1012    if draw.fx.additive {
1013        flags |= ADDITIVE;
1014    }
1015    if draw.fx.unpicked {
1016        flags |= UNPICKED;
1017    }
1018    if let Some(shift) = blur_shift(draw, &placed, fit) {
1019        if flags & FILL_MASK == FILL_IMAGE {
1020            flags = flags & !FILL_MASK | FILL_SOLID;
1021        }
1022        flags = (flags & !(LIT | SLAB)) | BLUR;
1023        material_params = [shift[0], shift[1], 0.0, 0.0];
1024        frame = [[0.0; 4]; 3];
1025        if matches!(kind, KIND_ICON_FILL | KIND_ICON_LINE) {
1026            shape = flat_bounds;
1027        }
1028        bounds = [
1029            flat_bounds[0],
1030            flat_bounds[1],
1031            flat_bounds[2] + shift[0].abs() * 0.5,
1032            flat_bounds[3] + shift[1].abs() * 0.5,
1033        ];
1034    }
1035    kind |= flags;
1036    let opacity = draw.opacity.min(1.0);
1037    let mut instance = Instance {
1038        axes: placed.axes,
1039        origin: [placed.origin[0], placed.origin[1], gradient[0], gradient[1]],
1040        bounds,
1041        shape,
1042        stroke: stroke_params,
1043        fill_top: top.0,
1044        fill_bottom: bottom.0,
1045        stroke_colour,
1046        icon,
1047        info: [kind, draw.id, pattern_colour, opacity.to_bits()],
1048        material: material_params,
1049        frame,
1050        corners,
1051    };
1052    if matches!(kind & 15, KIND_ICON_FILL | KIND_ICON_LINE) {
1053        let margin = instance.margin();
1054        instance.bounds[2] = (instance.bounds[2] - margin[0]).max(0.0);
1055        instance.bounds[3] = (instance.bounds[3] - margin[1]).max(0.0);
1056    }
1057    Some(instance)
1058}
1059
1060fn blur_shift(draw: &Draw, placed: &Placed, fit: &Fit) -> Option<[f32; 2]> {
1061    let blur = draw.fx.blur;
1062    if !(blur[0].is_finite() && blur[1].is_finite()) {
1063        return None;
1064    }
1065    if length2(blur) * fit.scale < BLUR_MIN_PIXELS {
1066        return None;
1067    }
1068    let [a, b, c, d] = placed.layout_axes;
1069    let det = a * d - c * b;
1070    Some([
1071        (d * blur[0] - c * blur[1]) / det,
1072        (-b * blur[0] + a * blur[1]) / det,
1073    ])
1074}
1075
1076pub fn pack_shadow(draw: &Draw, fit: &Fit, curve: &ShadowCurve, light: &Light) -> Option<Instance> {
1077    if draw.style.is_some() {
1078        return pack_shadow(&style::resolve(draw), fit, curve, light);
1079    }
1080    let (half, radii) = caster(draw)?;
1081    let placed = placed(&draw.transform, fit)?;
1082    let opacity = draw.opacity.min(1.0);
1083    let alpha = match draw.shadow {
1084        Shadow::Glow(_) => opacity,
1085        _ => paint_alpha(draw) * opacity,
1086    };
1087    if !positive(alpha) {
1088        return None;
1089    }
1090    let (colour, sigma, offset_local) = match draw.shadow {
1091        Shadow::None => return None,
1092        Shadow::Glow(glow) => (glow.colour, glow.sigma, [0.0, 0.0]),
1093        Shadow::Cast | Shadow::Turned => {
1094            let point = curve.at(draw.elevation)?;
1095            let direction = light.shadow;
1096            let length = length2(direction);
1097            let direction = if length > 0.0 {
1098                [direction[0] / length, direction[1] / length]
1099            } else {
1100                [0.0, 1.0]
1101            };
1102            let offset = [direction[0] * point.offset, direction[1] * point.offset];
1103            let local = if draw.shadow == Shadow::Turned {
1104                offset
1105            } else {
1106                let [a, b, c, d] = placed.layout_axes;
1107                let det = a * d - c * b;
1108                [
1109                    (d * offset[0] - c * offset[1]) / det,
1110                    (-b * offset[0] + a * offset[1]) / det,
1111                ]
1112            };
1113            (
1114                curve.colour.alpha(curve.colour.0[3] * point.alpha),
1115                point.sigma,
1116                local,
1117            )
1118        }
1119    };
1120    let alpha = alpha * colour.0[3];
1121    if !(sigma >= 0.0 && sigma.is_finite() && alpha > 0.0) {
1122        return None;
1123    }
1124    let [a, b, c, d] = placed.layout_axes;
1125    let unit = [length2([a, b]), length2([c, d])];
1126    let sigma_local = [(sigma / unit[0]).max(1e-4), (sigma / unit[1]).max(1e-4)];
1127    let reach = [4.0 * sigma_local[0], 4.0 * sigma_local[1]];
1128    Some(Instance {
1129        axes: placed.axes,
1130        origin: [placed.origin[0], placed.origin[1], 0.0, 0.0],
1131        bounds: [
1132            offset_local[0],
1133            offset_local[1],
1134            half[0] + reach[0],
1135            half[1] + reach[1],
1136        ],
1137        shape: [half[0], half[1], 0.0, 0.0],
1138        stroke: [0.0; 4],
1139        fill_top: colour.alpha(alpha).0,
1140        fill_bottom: [0.0; 4],
1141        stroke_colour: [0.0; 4],
1142        icon: [
1143            sigma_local[0],
1144            sigma_local[1],
1145            offset_local[0],
1146            offset_local[1],
1147        ],
1148        info: [KIND_SHADOW, 0, 0, 1.0f32.to_bits()],
1149        material: [0.0; 4],
1150        frame: [[0.0; 4]; 3],
1151        corners: radii,
1152    })
1153}
1154
1155#[derive(Clone, Copy, Debug, PartialEq)]
1156pub enum Step {
1157    Draw(usize),
1158    Layer(u16),
1159    EndLayer(u16),
1160}
1161
1162#[derive(Clone, Debug, Default)]
1163pub struct Order {
1164    pub steps: Vec<Step>,
1165    keys: Vec<(f32, usize, f32, usize)>,
1166    bases: Vec<(f32, usize)>,
1167}
1168
1169impl Order {
1170    pub fn build(&mut self, draws: &[Draw], groups: usize) -> Result<(), String> {
1171        self.steps.clear();
1172        self.keys.clear();
1173        self.bases.clear();
1174        self.bases.resize(groups, (f32::MAX, usize::MAX));
1175        for (index, draw) in draws.iter().enumerate() {
1176            if !draw.elevation.is_finite() {
1177                return Err(format!("draw {index} has a non-finite elevation"));
1178            }
1179            if let Some(group) = draw.group {
1180                let base = self
1181                    .bases
1182                    .get_mut(usize::from(group))
1183                    .ok_or_else(|| format!("draw {index} names group {group}, which is missing"))?;
1184                base.0 = base.0.min(draw.elevation);
1185                base.1 = base.1.min(index);
1186            }
1187        }
1188        for (index, draw) in draws.iter().enumerate() {
1189            self.keys.push(match draw.group {
1190                None => (draw.elevation, index, 0.0, index),
1191                Some(group) => {
1192                    let (base, first) = self.bases[usize::from(group)];
1193                    (base, first, draw.elevation, index)
1194                }
1195            });
1196        }
1197        self.keys.sort_unstable_by(|a, b| {
1198            a.0.total_cmp(&b.0)
1199                .then(a.1.cmp(&b.1))
1200                .then(a.2.total_cmp(&b.2))
1201                .then(a.3.cmp(&b.3))
1202        });
1203        let mut open: Option<u16> = None;
1204        for &(_, _, _, index) in &self.keys {
1205            let group = draws[index].group;
1206            if open != group {
1207                if let Some(previous) = open {
1208                    self.steps.push(Step::EndLayer(previous));
1209                }
1210                if let Some(next) = group {
1211                    self.steps.push(Step::Layer(next));
1212                }
1213                open = group;
1214            }
1215            self.steps.push(Step::Draw(index));
1216        }
1217        if let Some(previous) = open {
1218            self.steps.push(Step::EndLayer(previous));
1219        }
1220        Ok(())
1221    }
1222}