cranpose_ui_graphics/
shape_records.rs1use bytemuck::{Pod, Zeroable};
2
3use crate::ShapeRecord;
4
5#[repr(C)]
7#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
8pub struct ShapeRecordBody {
9 pub rect: [f32; 4],
11 pub color: [f32; 4],
13 pub stroke_width: f32,
15 pub flags: u32,
17 pub brush: u32,
19 pub placement: u32,
21 pub arc_geometry: [f32; 4],
23}
24
25#[repr(C)]
27#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
28pub struct ShapeRecordCurve {
29 pub radii: [f32; 4],
40 pub arc_normalized: [f32; 4],
43}
44
45#[repr(C)]
48#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)]
49struct RecordSource {
50 index: u32,
51 arguments: [f32; 4],
52}
53
54#[derive(Clone, Debug, Default, PartialEq)]
61pub struct ShapeRecords {
62 bodies: Vec<ShapeRecordBody>,
63 curves: Vec<ShapeRecordCurve>,
64 sources: Vec<RecordSource>,
66}
67
68impl ShapeRecords {
69 pub(crate) fn with_capacity(capacity: usize) -> Self {
70 Self {
71 bodies: Vec::with_capacity(capacity),
72 curves: Vec::with_capacity(capacity),
73 sources: Vec::new(),
74 }
75 }
76
77 pub fn len(&self) -> usize {
79 self.bodies.len()
80 }
81
82 pub fn is_empty(&self) -> bool {
84 self.bodies.is_empty()
85 }
86
87 pub fn bodies(&self) -> &[ShapeRecordBody] {
89 &self.bodies
90 }
91
92 pub fn curves(&self) -> &[ShapeRecordCurve] {
94 &self.curves
95 }
96
97 pub fn occludes(&self, index: usize) -> bool {
101 self.bodies
102 .get(index)
103 .zip(self.curves.get(index))
104 .is_some_and(|(body, curve)| crate::record::interior_occludes(body, curve))
105 }
106
107 pub fn get(&self, index: usize) -> Option<ShapeRecord> {
109 self.bodies
110 .get(index)
111 .map(|body| reconstruct(body, &self.curves[index], self.source(index)))
112 }
113
114 pub fn iter(&self) -> impl ExactSizeIterator<Item = ShapeRecord> + DoubleEndedIterator + '_ {
116 self.bodies
117 .iter()
118 .zip(&self.curves)
119 .enumerate()
120 .map(|(index, (body, curve))| reconstruct(body, curve, self.source(index)))
121 }
122
123 fn source(&self, index: usize) -> [f32; 4] {
126 let Ok(index) = u32::try_from(index) else {
127 return [0.0; 4];
128 };
129 self.sources
130 .binary_search_by_key(&index, |source| source.index)
131 .map_or([0.0; 4], |found| self.sources[found].arguments)
132 }
133
134 pub(crate) fn capacity(&self) -> usize {
135 self.bodies.capacity().min(self.curves.capacity())
136 }
137
138 pub(crate) fn heap_bytes(&self) -> usize {
139 self.bodies.capacity() * std::mem::size_of::<ShapeRecordBody>()
140 + self.curves.capacity() * std::mem::size_of::<ShapeRecordCurve>()
141 + self.sources.capacity() * std::mem::size_of::<RecordSource>()
142 }
143
144 pub(crate) fn source_bytes(&self) -> &[u8] {
145 bytemuck::cast_slice(&self.sources)
146 }
147
148 pub(crate) fn clear(&mut self) {
149 self.bodies.clear();
150 self.curves.clear();
151 self.sources.clear();
152 }
153
154 pub(crate) fn trim_growth(&mut self) {
155 trim_growth(&mut self.bodies);
156 trim_growth(&mut self.curves);
157 trim_growth(&mut self.sources);
158 }
159
160 pub(crate) fn reserve(&mut self, additional: usize) {
161 self.bodies.reserve(additional);
162 self.curves.reserve(additional);
163 }
164
165 pub(crate) fn push(
166 &mut self,
167 body: ShapeRecordBody,
168 curve: ShapeRecordCurve,
169 source: [f32; 4],
170 ) {
171 if source.iter().any(|argument| argument.to_bits() != 0)
172 && let Ok(index) = u32::try_from(self.bodies.len())
173 {
174 self.sources.push(RecordSource {
175 index,
176 arguments: source,
177 });
178 }
179 self.bodies.push(body);
180 self.curves.push(curve);
181 }
182}
183
184pub(crate) fn trim_growth<T>(items: &mut Vec<T>) {
188 let len = items.len();
189 if items.capacity() > len + len / 4 {
190 items.shrink_to(len + len / 8);
191 }
192}
193
194fn reconstruct(body: &ShapeRecordBody, curve: &ShapeRecordCurve, source: [f32; 4]) -> ShapeRecord {
195 ShapeRecord {
196 rect: body.rect,
197 radii: curve.radii,
198 color: body.color,
199 stroke_width: body.stroke_width,
200 flags: body.flags,
201 brush: body.brush,
202 reserved: body.placement,
203 arc: [
204 body.arc_geometry[0],
205 body.arc_geometry[1],
206 source[0],
207 source[1],
208 ],
209 arc_band: [
210 source[2],
211 source[3],
212 body.arc_geometry[2],
213 body.arc_geometry[3],
214 ],
215 arc_normalized: curve.arc_normalized,
216 }
217}
218
219#[cfg(test)]
220#[path = "tests/shape_records_tests.rs"]
221mod tests;