1use mraphics_core::{
2 Color, GadgetIndex, GeometryView, InstanceUpdater, Material, MeshLike, Mobject2DMaterial,
3 MraphicsID, MultiColoredMaterial, RenderInstance,
4};
5use nalgebra::{UnitVector3, Vector3};
6
7const PREVIOUS_ATTR_LABEL: &'static str = "mobject-2d-previous-attribute";
8const REVERSE_ATTR_LABEL: &'static str = "mobject-2d-reverse-attribute";
9const THICKNESS_LABEL: &'static str = "mobject-2d-thickness-uniform";
10
11#[derive(Debug)]
12pub struct Mobject2DPath {
13 pub vertices: Vec<[f32; 3]>,
14
15 pub stroked: bool,
16 pub filled: bool,
17
18 pub stroke_color: Color<f32>,
19 pub fill_color: Color<f32>,
20}
21
22impl Mobject2DPath {
23 pub fn new(vertices: Vec<[f32; 3]>) -> Self {
24 Self {
25 vertices,
26
27 stroked: false,
28 filled: false,
29
30 stroke_color: Color::from_hex_str(mraphics_core::constants::RED).unwrap(),
33 fill_color: Color::from_hex_str(mraphics_core::constants::WHITE).unwrap(),
34 }
35 }
36}
37
38pub struct Mobject2DStroke {
39 pub color: Color<f32>,
40 pub thickness: f32,
41 material: Mobject2DMaterial,
42}
43
44impl Mobject2DStroke {
45 fn new() -> Self {
46 Self {
47 color: Color::from_hex_str(mraphics_core::constants::RED).unwrap(),
49 thickness: 0.05,
50 material: Mobject2DMaterial::new(),
51 }
52 }
53
54 fn init_geometry_view(&self, view: &mut GeometryView) {
55 view.add_attribute(
56 mraphics_core::constants::POSITION_ATTR_LABEL,
57 mraphics_core::constants::POSITION_ATTR_INDEX,
58 vec![],
59 );
60
61 view.add_attribute(
62 PREVIOUS_ATTR_LABEL,
63 GadgetIndex {
64 group_index: 1,
65 binding_index: 3,
66 },
67 vec![],
68 );
69
70 view.add_attribute(
71 REVERSE_ATTR_LABEL,
72 GadgetIndex {
73 group_index: 1,
74 binding_index: 4,
75 },
76 vec![],
77 );
78
79 view.add_attribute(
80 mraphics_core::constants::COLOR_ATTR_LABEL,
81 mraphics_core::constants::COLOR_ATTR_INDEX,
82 vec![],
83 );
84
85 view.add_uniform(
86 THICKNESS_LABEL,
87 GadgetIndex {
88 group_index: 1,
89 binding_index: 5,
90 },
91 vec![],
92 );
93 }
94
95 fn update_instance(&self, paths: &Vec<Mobject2DPath>, instance: &mut RenderInstance) {
96 let view = &mut instance.geometry;
97
98 let mut vertices = Vec::new();
99 let mut previous = Vec::new();
100 let mut color = Vec::new();
101 let mut reverse = Vec::new();
102
103 fn to_homogeneous(point: &[f32; 3]) -> [f32; 4] {
104 [point[0], point[1], point[2], 1.0]
105 }
106
107 let mut build_path = |path: &Mobject2DPath| {
108 let points = &path.vertices;
109
110 if points.is_empty() || points.len() < 2 {
112 return;
113 }
114
115 for i in 1..points.len() {
116 let start = &to_homogeneous(&points[i]);
117 let end = &to_homogeneous(&points[i - 1]);
118
119 vertices.extend_from_slice(start);
120 vertices.extend_from_slice(start);
121 vertices.extend_from_slice(end);
122 vertices.extend_from_slice(start);
123 vertices.extend_from_slice(end);
124 vertices.extend_from_slice(end);
125
126 previous.extend_from_slice(end);
127 previous.extend_from_slice(end);
128 previous.extend_from_slice(start);
129 previous.extend_from_slice(end);
130 previous.extend_from_slice(start);
131 previous.extend_from_slice(start);
132
133 color.extend_from_slice(&path.stroke_color);
134 color.extend_from_slice(&path.stroke_color);
135 color.extend_from_slice(&path.stroke_color);
136 color.extend_from_slice(&path.stroke_color);
137 color.extend_from_slice(&path.stroke_color);
138 color.extend_from_slice(&path.stroke_color);
139
140 reverse.extend_from_slice(&[-1., 1., 1., 1., 1., -1.]);
141 }
142 };
143
144 for path in paths {
145 if path.stroked {
146 build_path(&path);
147 }
148 }
149
150 view.set_attribute(
152 mraphics_core::constants::POSITION_ATTR_LABEL,
153 Vec::from(bytemuck::cast_slice::<f32, u8>(&vertices)),
154 )
155 .unwrap();
156 view.set_attribute(
157 PREVIOUS_ATTR_LABEL,
158 Vec::from(bytemuck::cast_slice::<f32, u8>(&previous)),
159 )
160 .unwrap();
161 view.set_attribute(
162 REVERSE_ATTR_LABEL,
163 Vec::from(bytemuck::cast_slice::<f32, u8>(&reverse)),
164 )
165 .unwrap();
166 view.set_attribute(
167 mraphics_core::constants::COLOR_ATTR_LABEL,
168 Vec::from(bytemuck::cast_slice::<f32, u8>(&color)),
169 )
170 .unwrap();
171 view.set_uniform(
172 THICKNESS_LABEL,
173 Vec::from(bytemuck::cast_slice::<f32, u8>(&[self.thickness])),
174 )
175 .unwrap();
176
177 let vertex_count = (vertices.len() / 4) as u32;
178
179 view.indices = mraphics_core::GeometryIndices::Sequential(vertex_count);
180
181 if vertex_count == 0 {
182 instance.visible = false;
183 } else {
184 instance.visible = true;
185 }
186 }
187}
188
189pub struct Mobject2DFill {
190 pub color: Color<f32>,
191 material: MultiColoredMaterial,
192}
193
194impl Mobject2DFill {
195 fn new() -> Self {
196 Self {
197 color: Color::from_hex_str(mraphics_core::constants::WHITE).unwrap(),
199
200 material: MultiColoredMaterial::new(),
201 }
202 }
203
204 fn init_geometry_view(&self, view: &mut GeometryView) {
205 view.add_attribute(
206 mraphics_core::constants::POSITION_ATTR_LABEL,
207 mraphics_core::constants::POSITION_ATTR_INDEX,
208 vec![],
209 );
210 view.add_attribute(
211 mraphics_core::constants::COLOR_ATTR_LABEL,
212 mraphics_core::constants::COLOR_ATTR_INDEX,
213 vec![],
214 );
215 }
216
217 fn update_instance(&self, paths: &Vec<Mobject2DPath>, instance: &mut RenderInstance) {
218 let view = &mut instance.geometry;
219
220 let mut vertices = Vec::new();
221 let mut colors = Vec::new();
222
223 fn to_homogeneous(point: &[f32; 3]) -> [f32; 4] {
224 [point[0], point[1], point[2], 1.0]
225 }
226
227 fn build_path(path: &Mobject2DPath, vertices: &mut Vec<f32>, colors: &mut Vec<f32>) {
228 let points = &path.vertices;
229
230 if points.is_empty() || points.len() < 3 {
232 return;
233 }
234
235 let first = &points[0];
236
237 for i in 1..(points.len() - 1) {
241 vertices.extend_from_slice(&to_homogeneous(first));
242 vertices.extend_from_slice(&to_homogeneous(&points[i]));
243 vertices.extend_from_slice(&to_homogeneous(&points[i + 1]));
244
245 colors.extend_from_slice(&path.fill_color);
246 colors.extend_from_slice(&path.fill_color);
247 colors.extend_from_slice(&path.fill_color);
248 }
249 }
250
251 for path in paths {
252 if path.filled {
253 build_path(&path, &mut vertices, &mut colors);
254 }
255 }
256
257 view.set_attribute(
259 mraphics_core::constants::POSITION_ATTR_LABEL,
260 Vec::from(bytemuck::cast_slice::<f32, u8>(&vertices)),
261 )
262 .unwrap();
263 view.set_attribute(
264 mraphics_core::constants::COLOR_ATTR_LABEL,
265 Vec::from(bytemuck::cast_slice::<f32, u8>(&colors)),
266 )
267 .unwrap();
268
269 let vertex_count = (vertices.len() / 4) as u32;
270
271 view.indices = mraphics_core::GeometryIndices::Sequential(vertex_count);
272
273 if vertex_count == 0 {
274 instance.visible = false;
275 } else {
276 instance.visible = true;
277 }
278 }
279}
280
281pub struct Mobject2DArcDescriptor {
288 pub radius: f32,
289 pub start_rad: f32,
290 pub end_rad: f32,
291 pub clockwise: bool,
292 pub segment_num: u32,
293
294 pub center: [f32; 3],
295
296 pub x_axis: UnitVector3<f32>,
298
299 pub y_axis: UnitVector3<f32>,
301}
302
303impl Default for Mobject2DArcDescriptor {
304 fn default() -> Self {
305 Self {
306 radius: 1.0,
307 start_rad: 0.0,
308 end_rad: std::f32::consts::PI,
309 clockwise: false,
310 segment_num: 25,
311
312 center: [0.0, 0.0, 0.0],
313
314 x_axis: UnitVector3::new_normalize(Vector3::x()),
315 y_axis: UnitVector3::new_normalize(Vector3::y()),
316 }
317 }
318}
319
320pub struct Mobject2D {
321 identifier: MraphicsID,
322
323 vertices: Vec<[f32; 3]>,
324 pub paths: Vec<Mobject2DPath>,
325
326 pub stroke: Mobject2DStroke,
327 pub fill: Mobject2DFill,
328}
329
330impl Mobject2D {
331 pub fn new() -> Self {
332 Self {
333 identifier: MraphicsID::acquire(),
334
335 vertices: Vec::new(),
336 paths: Vec::new(),
337
338 stroke: Mobject2DStroke::new(),
339 fill: Mobject2DFill::new(),
340 }
341 }
342
343 pub fn move_to(&mut self, point: [f32; 3]) {
349 self.finish();
350
351 self.vertices.push(point);
352 }
353
354 pub fn line_to(&mut self, point: [f32; 3]) {
360 self.vertices.push(point);
361 }
362
363 pub fn arc(&mut self, desc: &Mobject2DArcDescriptor) {
370 self.finish();
371
372 let &Mobject2DArcDescriptor {
373 radius,
374 mut start_rad,
375 mut end_rad,
376 mut clockwise,
377 segment_num,
378 center,
379 x_axis,
380 y_axis,
381 } = desc;
382
383 if radius == 0.0 || start_rad == end_rad {
384 return;
385 }
386
387 let center = Vector3::from_column_slice(¢er);
388
389 if start_rad > end_rad {
390 start_rad = end_rad;
391 end_rad = desc.start_rad;
392 clockwise = !clockwise;
393 }
394
395 let unit = if clockwise {
396 (end_rad - start_rad - std::f32::consts::PI * 2.0) / segment_num as f32
397 } else {
398 (end_rad - start_rad) / segment_num as f32
399 };
400
401 let (x_axis, y_axis) = (x_axis.into_inner(), y_axis.into_inner());
402
403 for i in 0..=segment_num {
404 let angle = start_rad + i as f32 * unit;
405 let position = center + x_axis * angle.cos() * radius + y_axis * angle.sin() * radius;
406 self.vertices.push([position[0], position[1], position[2]]);
407 }
408 }
409
410 pub fn stroke(&mut self) {
415 self.finish();
416
417 let len = self.paths.len();
418
419 if len == 0 {
420 return;
421 }
422
423 self.paths[len - 1].stroked = true;
424 self.paths[len - 1].stroke_color = self.stroke.color.clone();
425 }
426
427 pub fn fill(&mut self) {
432 self.finish();
433
434 let len = self.paths.len();
435
436 if len == 0 {
437 return;
438 }
439
440 self.paths[len - 1].filled = true;
441 self.paths[len - 1].fill_color = self.fill.color.clone();
442 }
443
444 pub fn finish(&mut self) {
450 if self.vertices.len() == 0 {
451 return;
452 }
453
454 self.paths
455 .push(Mobject2DPath::new(std::mem::take(&mut self.vertices)));
456 }
457}
458
459impl InstanceUpdater for Mobject2D {
460 fn update_instance(&self, instance: &mut RenderInstance) {
461 self.fill.update_instance(&self.paths, instance);
462
463 self.stroke
467 .update_instance(&self.paths, &mut instance.children[0]);
468 }
469}
470
471impl MeshLike for Mobject2D {
472 fn identifier(&self) -> MraphicsID {
473 self.identifier
474 }
475
476 fn build_instance(&self) -> mraphics_core::RenderInstance {
477 let mut instance = RenderInstance::new(self.identifier, &self.fill.material);
478 let mut stroke = RenderInstance::new(MraphicsID::acquire(), &self.stroke.material);
479
480 self.fill.init_geometry_view(&mut instance.geometry);
481 self.stroke.init_geometry_view(&mut stroke.geometry);
482
483 self.fill.material.update_view(&mut instance.material);
484 self.stroke.material.update_view(&mut stroke.material);
485
486 instance.add_child(stroke);
487
488 instance
489 }
490}