1use kurbo::{BezPath, Rect as KRect, Shape, Vec2};
6use rstar::{AABB, RTree, RTreeObject};
7
8use crate::render::Draw;
9
10use super::*;
11
12pub struct Command {
15 pub draw: Draw,
16 pub clip: Id,
17 pub effect: Option<Id>,
18 pub bounds: KRect,
20}
21
22pub struct Builder<'a> {
25 pub(super) trees: &'a mut Trees,
26 pub(super) transform: Id,
27 pub(super) stack: Vec<(Id, Option<Id>)>,
28 pub(super) commands: Vec<Command>,
29 pub(super) made: Made,
31}
32
33#[derive(Default, Clone)]
35pub struct Made {
36 pub clips: Vec<Id>,
37 pub effects: Vec<Id>,
38}
39
40impl<'a> Builder<'a> {
41 pub fn new(trees: &'a mut Trees, transform: Id, clip: Id, effect: Option<Id>) -> Builder<'a> {
43 Builder {
44 trees,
45 transform,
46 stack: vec![(clip, effect)],
47 commands: Vec::new(),
48 made: Made::default(),
49 }
50 }
51
52 pub fn draw(&mut self, draw: Draw) {
54 let bounds = match &draw {
55 Draw::Fill(path, _) => path.bounding_box(),
56 Draw::Stroke(path, width, _) => {
57 let half = *width as f64 / 2.0;
58 path.bounding_box().inflate(half, half)
59 }
60 Draw::Glyphs(_, size, _, _, glyphs) => glyph_bounds(*size, glyphs),
61 };
62 let (clip, effect) = *self.stack.last().expect("balanced layers");
63 self.commands.push(Command {
65 draw,
66 clip,
67 effect,
68 bounds: bounds.inflate(1.0, 1.0),
69 });
70 }
71
72 pub fn push_clip(&mut self, path: BezPath) {
74 let (clip, effect) = *self.stack.last().expect("balanced layers");
75 let id = self.trees.clips.add(Clip {
76 parent: Some(clip),
77 transform: self.transform,
78 path,
79 });
80 self.made.clips.push(id);
81 self.stack.push((id, effect));
82 }
83
84 pub fn push_layer(&mut self, blend: Blend, bounds: Option<BezPath>) {
86 let (clip, effect) = *self.stack.last().expect("balanced layers");
87 let id = self.trees.effects.add(Effect {
88 parent: effect,
89 clip,
90 transform: self.transform,
91 blend,
92 bounds,
93 });
94 self.made.effects.push(id);
95 self.stack.push((clip, Some(id)));
96 }
97
98 pub fn pop(&mut self) {
100 if self.stack.len() > 1 {
101 self.stack.pop();
102 }
103 }
104
105 pub fn finish(self) -> (Vec<Command>, Made) {
106 (self.commands, self.made)
107 }
108}
109
110pub(super) fn glyph_bounds(size: f32, glyphs: &[crate::render::Glyph]) -> KRect {
114 let mut r: Option<KRect> = None;
115 for g in glyphs {
116 let p = KRect::new(g.x as f64, g.y as f64, g.x as f64, g.y as f64);
117 r = Some(r.map_or(p, |r| r.union(p)));
118 }
119 let em = size as f64;
120 r.unwrap_or_default().inflate(em, em)
121}
122
123pub(super) struct Local {
125 pub(super) at: usize,
126 pub(super) bounds: KRect,
127}
128
129impl RTreeObject for Local {
130 type Envelope = AABB<[f64; 2]>;
131 fn envelope(&self) -> Self::Envelope {
132 aabb(self.bounds)
133 }
134}
135
136pub(super) fn aabb(r: KRect) -> AABB<[f64; 2]> {
137 AABB::from_corners([r.x0, r.y0], [r.x1, r.y1])
138}
139
140pub use crate::tree::Order;
143
144#[derive(Clone, Debug, PartialEq)]
147pub struct Hit {
148 pub action: String,
149 pub area: KRect,
150}
151
152pub struct Chunk {
154 pub transform: Id,
155 pub space: Space,
157 pub origin: Vec2,
159 pub commands: Vec<Command>,
160 pub(super) index: RTree<Local>,
161 pub cut: Option<KRect>,
164 pub covers: Vec<KRect>,
167 pub bounds: Option<KRect>,
170 pub action: Option<(String, KRect)>,
173 pub made: Made,
175}
176
177impl Chunk {
178 #[allow(clippy::too_many_arguments)]
179 pub fn new(
180 transform: Id,
181 space: Space,
182 origin: Vec2,
183 (commands, made): (Vec<Command>, Made),
184 covers: Vec<KRect>,
185 action: Option<(String, KRect)>,
186 cut: Option<KRect>,
187 ) -> Chunk {
188 let index = RTree::bulk_load(
189 commands
190 .iter()
191 .enumerate()
192 .map(|(at, c)| Local {
193 at,
194 bounds: c.bounds,
195 })
196 .collect(),
197 );
198 let mut chunk = Chunk {
199 transform,
200 space,
201 origin,
202 commands,
203 index,
204 cut,
205 covers,
206 bounds: None,
207 action,
208 made,
209 };
210 chunk.place(origin, cut);
211 chunk
212 }
213
214 pub fn moved(mut self, origin: Vec2, cut: Option<KRect>) -> Chunk {
216 self.place(origin, cut);
217 self
218 }
219
220 pub(super) fn place(&mut self, origin: Vec2, cut: Option<KRect>) {
221 let local = self
222 .commands
223 .iter()
224 .map(|c| c.bounds)
225 .reduce(|a, b| a.union(b));
226 self.bounds = local.and_then(|r| cut_to(r + origin, cut));
227 self.origin = origin;
228 self.cut = cut;
229 }
230
231 pub fn cover(&self) -> Option<KRect> {
234 self.covers
235 .iter()
236 .filter_map(|&r| cut_to(r + self.origin, self.cut))
237 .reduce(|a, b| a.union(b))
238 }
239
240 pub(super) fn reach(&self) -> Option<KRect> {
243 let hit = self
244 .hit()
245 .map(|h| h.area)
246 .filter(|r| r.width() > 0.0 && r.height() > 0.0);
247 match (self.bounds, hit) {
248 (Some(a), Some(b)) => Some(a.union(b)),
249 (a, b) => a.or(b),
250 }
251 }
252}
253
254pub(super) fn cut_to(r: KRect, cut: Option<KRect>) -> Option<KRect> {
256 let r = cut.map_or(r, |c| r.intersect(c));
257 (r.width() > 0.0 && r.height() > 0.0).then_some(r)
258}