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 reserve(&mut self, additional: usize) {
155 self.bodies.reserve(additional);
156 self.curves.reserve(additional);
157 }
158
159 pub(crate) fn push(
160 &mut self,
161 body: ShapeRecordBody,
162 curve: ShapeRecordCurve,
163 source: [f32; 4],
164 ) {
165 if source.iter().any(|argument| argument.to_bits() != 0)
166 && let Ok(index) = u32::try_from(self.bodies.len())
167 {
168 self.sources.push(RecordSource {
169 index,
170 arguments: source,
171 });
172 }
173 self.bodies.push(body);
174 self.curves.push(curve);
175 }
176}
177
178fn reconstruct(body: &ShapeRecordBody, curve: &ShapeRecordCurve, source: [f32; 4]) -> ShapeRecord {
179 ShapeRecord {
180 rect: body.rect,
181 radii: curve.radii,
182 color: body.color,
183 stroke_width: body.stroke_width,
184 flags: body.flags,
185 brush: body.brush,
186 reserved: body.placement,
187 arc: [
188 body.arc_geometry[0],
189 body.arc_geometry[1],
190 source[0],
191 source[1],
192 ],
193 arc_band: [
194 source[2],
195 source[3],
196 body.arc_geometry[2],
197 body.arc_geometry[3],
198 ],
199 arc_normalized: curve.arc_normalized,
200 }
201}
202
203#[cfg(test)]
204#[path = "tests/shape_records_tests.rs"]
205mod tests;