honeycomb_render/
import_map.rs

1use bevy::prelude::{Bundle, Commands, Component, Res, Resource, Vec3};
2use bevy::utils::HashMap;
3use honeycomb_core::cmap::NULL_DART_ID;
4use honeycomb_core::{
5    cmap::{
6        CMap2, CMap3, DartIdType, EdgeIdType, FaceIdType, OrbitPolicy, VertexIdType, VolumeIdType,
7    },
8    geometry::CoordsFloat,
9};
10use itertools::Itertools;
11
12// --- shared data
13
14/// Combinatorial map to render.
15#[derive(Resource)]
16pub struct Map<T: CoordsFloat>(pub CMap2<T>);
17
18/// Combinatorial map to render.
19#[derive(Resource)]
20pub struct Map3<T: CoordsFloat>(pub CMap3<T>);
21
22/// Collection of a map's vertices.
23#[derive(Resource)]
24pub struct MapVertices(pub Vec<Vec3>);
25
26/// Collection of normals, organized per faces of a map.
27#[derive(Resource)]
28pub struct FaceNormals(pub HashMap<(FaceIdType, usize), Vec3>);
29
30/// Collection of normals, organized per volumes of a map.
31#[derive(Resource)]
32pub struct VolumeNormals(pub HashMap<(VolumeIdType, usize), Vec3>);
33
34// --- bundles
35
36/// Bundle used to create entities modeling dart bodies.
37#[derive(Bundle, Clone)]
38pub struct DartBundle {
39    pub(crate) id: DartId,
40    pub(crate) vertex_id: VertexId,
41    pub(crate) edge_id: EdgeId,
42    pub(crate) face_id: FaceId,
43    pub(crate) volume_id: VolumeId,
44    pub(crate) dart: Dart,
45}
46
47/// Bundle used to create entities modeling vertices.
48#[derive(Bundle, Clone)]
49pub struct VertexBundle {
50    pub(crate) id: VertexId,
51    pub(crate) vertex: Vertex,
52}
53
54/// Bundle used to create entities modeling edges.
55#[derive(Bundle, Clone)]
56pub struct EdgeBundle {
57    pub(crate) id: EdgeId,
58    pub(crate) edge: Edge,
59}
60
61/// Bundle used to create entities modeling faces.
62#[derive(Bundle, Clone)]
63pub struct FaceBundle {
64    pub(crate) id: FaceId,
65    pub(crate) face: Face,
66}
67
68// --- individual components
69
70/// Dart ID component.
71#[derive(Component, Clone)]
72pub struct DartId(pub DartIdType);
73
74/// Vertex ID component.
75#[derive(Component, Clone)]
76pub struct VertexId(pub VertexIdType);
77
78/// Edge ID component.
79#[derive(Component, Clone)]
80pub struct EdgeId(pub EdgeIdType);
81
82/// Face ID component.
83#[derive(Component, Clone)]
84pub struct FaceId(pub FaceIdType);
85
86/// Volume ID component.
87#[derive(Component, Clone)]
88pub struct VolumeId(pub VolumeIdType);
89
90/// Dart head component.
91#[derive(Component, Clone)]
92pub struct Dart {
93    pub(crate) start: usize,
94    pub(crate) end: usize,
95}
96
97/// Beta component.
98#[derive(Component, Clone)]
99pub struct Beta(pub u8, pub usize, pub usize); // beta id, v0_id, v1_id ?
100
101/// Vertex component.
102#[derive(Component, Clone)]
103pub struct Vertex(pub usize); // map id, vid
104
105/// Edge component.
106#[derive(Component, Clone)]
107pub struct Edge(pub usize, pub usize); // v0_id, v1_id
108
109/// Face component.
110#[derive(Component, Clone)]
111pub struct Face(pub Vec<usize>); // vertex list
112
113/// Volume component.
114#[derive(Component, Clone)]
115pub struct Volume;
116
117// --- startup routine
118
119/// Build ECS data from a combinatorial map object.
120///
121/// # Panics
122///
123/// This function will panic if there is a topological vertex with no associated coordinates.
124#[allow(clippy::too_many_lines)]
125pub fn extract_data_from_map<T: CoordsFloat>(mut commands: Commands, cmap: Res<Map<T>>) {
126    let cmap = &cmap.0;
127    let map_vertices: Vec<_> = cmap.iter_vertices().collect();
128    let map_edges: Vec<_> = cmap.iter_edges().collect();
129    let map_faces: Vec<_> = cmap.iter_faces().collect();
130
131    let mut index_map: HashMap<VertexIdType, usize> = HashMap::with_capacity(cmap.n_vertices());
132
133    let vertex_vals: Vec<Vec3> = map_vertices
134        .iter()
135        .enumerate()
136        .map(|(idx, vid)| {
137            index_map.insert(*vid, idx);
138            let v = cmap
139                .force_read_vertex(*vid)
140                .expect("E: found a topological vertex with no associated coordinates");
141            // sane unwraps; will crash if the coordinates cannot be converted to f32
142            Vec3::from((v.0.to_f32().unwrap(), v.1.to_f32().unwrap(), 0.0))
143        })
144        .collect();
145
146    let vertices: Vec<VertexBundle> = map_vertices
147        .iter()
148        .map(|id| VertexBundle {
149            id: VertexId(*id),
150            vertex: Vertex(index_map[id]),
151        })
152        .collect();
153
154    let edges: Vec<EdgeBundle> = map_edges
155        .iter()
156        .map(|id| {
157            let v1id = cmap.vertex_id(*id as DartIdType);
158            let v2id = if cmap.is_i_free::<2>(*id as DartIdType) {
159                cmap.vertex_id(cmap.beta::<1>(*id as DartIdType))
160            } else {
161                cmap.vertex_id(cmap.beta::<2>(*id as DartIdType))
162            };
163            EdgeBundle {
164                id: EdgeId(*id),
165                edge: Edge(index_map[&v1id], index_map[&v2id]),
166            }
167        })
168        .collect();
169
170    let mut face_normals = HashMap::new();
171    let mut darts: Vec<DartBundle> = Vec::new();
172
173    let faces: Vec<FaceBundle> = map_faces
174        .iter()
175        .map(|id| {
176            let vertex_ids: Vec<usize> = cmap
177                .orbit(OrbitPolicy::Custom(&[1]), *id as DartIdType)
178                .map(|dart_id| index_map[&cmap.vertex_id(dart_id)])
179                .collect();
180            let n_v = vertex_ids.len();
181            {
182                let (ver_in, ver, ver_out) = (&vertex_ids[n_v - 1], &vertex_ids[0], &vertex_ids[1]);
183                let (vec_in, vec_out) = (
184                    vertex_vals[*ver] - vertex_vals[*ver_in],
185                    vertex_vals[*ver_out] - vertex_vals[*ver],
186                );
187                // vec_in/out belong to X/Y plane => .cross(Z) == normal in the plane
188                // a first normalization is required because both edges should weight equally
189                let normal = (vec_in.cross(Vec3::Z).normalize()
190                    + vec_out.cross(Vec3::Z).normalize())
191                .normalize();
192                face_normals.insert((*id, *ver), normal);
193            }
194            vertex_ids.windows(3).for_each(|wdw| {
195                let [ver_in, ver, ver_out] = wdw else {
196                    unreachable!("i kneel");
197                };
198                let (vec_in, vec_out) = (
199                    vertex_vals[*ver] - vertex_vals[*ver_in],
200                    vertex_vals[*ver_out] - vertex_vals[*ver],
201                );
202                let normal = (vec_in.cross(Vec3::Z).normalize()
203                    + vec_out.cross(Vec3::Z).normalize())
204                .normalize();
205                face_normals.insert((*id, *ver), normal);
206            });
207            {
208                let (ver_in, ver, ver_out) =
209                    (&vertex_ids[n_v - 2], &vertex_ids[n_v - 1], &vertex_ids[0]);
210                let (vec_in, vec_out) = (
211                    vertex_vals[*ver] - vertex_vals[*ver_in],
212                    vertex_vals[*ver_out] - vertex_vals[*ver],
213                );
214                let normal = (vec_in.cross(Vec3::Z).normalize()
215                    + vec_out.cross(Vec3::Z).normalize())
216                .normalize();
217                face_normals.insert((*id, *ver), normal);
218            }
219
220            // common dart iterator
221            let mut tmp = cmap
222                .orbit(OrbitPolicy::Custom(&[1]), *id as DartIdType)
223                .map(|dart_id| (dart_id, index_map[&cmap.vertex_id(dart_id)]))
224                .collect::<Vec<_>>();
225            tmp.push(tmp[0]); // trick for the `.windows` call
226
227            // dart bodies
228            let bodies = tmp.windows(2).map(|wd| {
229                let [(dart_id, v1_id), (_, v2_id)] = wd else {
230                    unreachable!("i kneel");
231                };
232
233                DartBundle {
234                    id: DartId(*dart_id),
235                    vertex_id: VertexId(cmap.vertex_id(*dart_id)),
236                    edge_id: EdgeId(cmap.edge_id(*dart_id)),
237                    face_id: FaceId(*id),
238                    volume_id: VolumeId(1),
239                    dart: Dart {
240                        start: *v1_id,
241                        end: *v2_id,
242                    },
243                }
244            });
245
246            darts.extend(bodies);
247
248            FaceBundle {
249                id: FaceId(*id),
250                face: Face(vertex_ids),
251            }
252        })
253        .collect();
254
255    commands.insert_resource(MapVertices(vertex_vals));
256    commands.insert_resource(FaceNormals(face_normals));
257
258    for bundle in darts {
259        commands.spawn(bundle);
260    }
261    for bundle in vertices {
262        commands.spawn(bundle);
263    }
264    for bundle in edges {
265        commands.spawn(bundle);
266    }
267    for bundle in faces {
268        commands.spawn(bundle);
269    }
270}
271
272/// Build ECS data from a combinatorial map object.
273///
274/// # Panics
275///
276/// This function will panic if there is a topological vertex with no associated coordinates.
277#[allow(clippy::too_many_lines)]
278pub fn extract_data_from_3d_map<T: CoordsFloat>(mut commands: Commands, cmap: Res<Map3<T>>) {
279    let cmap = &cmap.0;
280    let map_vertices: Vec<_> = cmap.iter_vertices().collect();
281    let map_edges: Vec<_> = cmap.iter_edges().collect();
282    let map_faces: Vec<_> = cmap.iter_faces().collect();
283    let map_volumes: Vec<_> = cmap.iter_volumes().collect();
284
285    let mut index_map: HashMap<VertexIdType, usize> = HashMap::with_capacity(cmap.n_vertices());
286
287    let vertex_vals: Vec<Vec3> = map_vertices
288        .iter()
289        .enumerate()
290        .map(|(idx, vid)| {
291            index_map.insert(*vid, idx);
292            let v = cmap
293                .force_read_vertex(*vid)
294                .expect("E: found a topological vertex with no associated coordinates");
295            // sane unwraps; will crash if the coordinates cannot be converted to f32
296            Vec3::from((
297                v.0.to_f32().unwrap(),
298                v.1.to_f32().unwrap(),
299                v.2.to_f32().unwrap(),
300            ))
301        })
302        .collect();
303
304    let vertices: Vec<VertexBundle> = map_vertices
305        .iter()
306        .map(|id| VertexBundle {
307            id: VertexId(*id),
308            vertex: Vertex(index_map[id]),
309        })
310        .collect();
311
312    let edges: Vec<EdgeBundle> = map_edges
313        .iter()
314        .map(|id| {
315            let v1id = cmap.vertex_id(*id as DartIdType);
316            let v2id = if cmap.is_i_free::<3>(*id as DartIdType) {
317                if cmap.is_i_free::<2>(*id as DartIdType) {
318                    cmap.vertex_id(cmap.beta::<1>(*id as DartIdType))
319                } else {
320                    cmap.vertex_id(cmap.beta::<2>(*id as DartIdType))
321                }
322            } else {
323                cmap.vertex_id(cmap.beta::<3>(*id as DartIdType))
324            };
325            EdgeBundle {
326                id: EdgeId(*id),
327                edge: Edge(index_map[&v1id], index_map[&v2id]),
328            }
329        })
330        .collect();
331
332    let mut face_normals = HashMap::new();
333    let mut darts: Vec<DartBundle> = Vec::new();
334
335    let faces: Vec<FaceBundle> = map_faces
336        .iter()
337        .map(|id| {
338            let vertex_ids: Vec<usize> = cmap
339                .orbit(OrbitPolicy::Custom(&[1]), *id as DartIdType) // cannot use FaceLinear here due to doubled darts in 3D
340                .map(|dart_id| index_map[&cmap.vertex_id(dart_id)])
341                .collect();
342            let n_v = vertex_ids.len();
343            {
344                let (ver_in, ver, ver_out) = (&vertex_ids[n_v - 1], &vertex_ids[0], &vertex_ids[1]);
345                let (vec_in, vec_out) = (
346                    vertex_vals[*ver] - vertex_vals[*ver_in],
347                    vertex_vals[*ver_out] - vertex_vals[*ver],
348                );
349                // we need to compute the normql formed by the two vectors in 3D
350                let plane_normal = vec_in.cross(vec_out).normalize();
351                let normal = (vec_in.cross(plane_normal).normalize()
352                    + vec_out.cross(plane_normal).normalize())
353                .normalize();
354                face_normals.insert((*id, *ver), normal);
355            }
356            vertex_ids.windows(3).for_each(|wdw| {
357                let [ver_in, ver, ver_out] = wdw else {
358                    unreachable!("i kneel");
359                };
360                let (vec_in, vec_out) = (
361                    vertex_vals[*ver] - vertex_vals[*ver_in],
362                    vertex_vals[*ver_out] - vertex_vals[*ver],
363                );
364                // we need to compute the normql formed by the two vectors in 3D
365                let plane_normal = vec_in.cross(vec_out).normalize();
366                let normal = (vec_in.cross(plane_normal).normalize()
367                    + vec_out.cross(plane_normal).normalize())
368                .normalize();
369                face_normals.insert((*id, *ver), normal);
370            });
371            {
372                let (ver_in, ver, ver_out) =
373                    (&vertex_ids[n_v - 2], &vertex_ids[n_v - 1], &vertex_ids[0]);
374                let (vec_in, vec_out) = (
375                    vertex_vals[*ver] - vertex_vals[*ver_in],
376                    vertex_vals[*ver_out] - vertex_vals[*ver],
377                );
378                // we need to compute the normql formed by the two vectors in 3D
379                let plane_normal = vec_in.cross(vec_out).normalize();
380                let normal = (vec_in.cross(plane_normal).normalize()
381                    + vec_out.cross(plane_normal).normalize())
382                .normalize();
383                face_normals.insert((*id, *ver), normal);
384            }
385
386            // common dart iterator
387            let mut tmp = cmap
388                .orbit(OrbitPolicy::Custom(&[1]), *id as DartIdType)
389                .map(|dart_id| (dart_id, index_map[&cmap.vertex_id(dart_id)]))
390                .collect::<Vec<_>>();
391            tmp.push(tmp[0]); // trick for the `.windows` call
392
393            // dart bodies
394            let bodies = tmp.windows(2).map(|wd| {
395                let [(dart_id, v1_id), (_, v2_id)] = wd else {
396                    unreachable!("i kneel");
397                };
398
399                DartBundle {
400                    id: DartId(*dart_id),
401                    vertex_id: VertexId(cmap.vertex_id(*dart_id)),
402                    edge_id: EdgeId(cmap.edge_id(*dart_id)),
403                    face_id: FaceId(*id),
404                    volume_id: VolumeId(cmap.volume_id(*dart_id)),
405                    dart: Dart {
406                        start: *v1_id,
407                        end: *v2_id,
408                    },
409                }
410            });
411
412            darts.extend(bodies);
413
414            // common dart iterator
415            let mut tmp2 = cmap
416                .orbit(OrbitPolicy::Custom(&[1]), cmap.beta::<3>(*id as DartIdType))
417                .filter_map(|dart_id| {
418                    if dart_id == NULL_DART_ID {
419                        return None;
420                    }
421                    Some((dart_id, index_map[&cmap.vertex_id(dart_id)]))
422                })
423                .collect::<Vec<_>>();
424            if !tmp2.is_empty() {
425                tmp2.push(tmp2[0]); // trick for the `.windows` call
426            }
427            // dart bodies
428            let bodies2 = tmp2.windows(2).map(|wd| {
429                let [(dart_id, v1_id), (_, v2_id)] = wd else {
430                    unreachable!("i kneel");
431                };
432
433                DartBundle {
434                    id: DartId(*dart_id),
435                    vertex_id: VertexId(cmap.vertex_id(*dart_id)),
436                    edge_id: EdgeId(cmap.edge_id(*dart_id)),
437                    face_id: FaceId(*id),
438                    volume_id: VolumeId(cmap.volume_id(*dart_id)),
439                    dart: Dart {
440                        start: *v1_id,
441                        end: *v2_id,
442                    },
443                }
444            });
445
446            darts.extend(bodies2);
447
448            FaceBundle {
449                id: FaceId(*id),
450                face: Face(vertex_ids),
451            }
452        })
453        .collect();
454
455    let mut volume_normals = HashMap::new();
456
457    for vol in map_volumes {
458        let darts: Vec<_> = cmap.orbit(OrbitPolicy::Volume, vol as DartIdType).collect();
459        let norms: HashMap<_, _> = darts
460            .iter()
461            .unique_by(|&d| cmap.face_id(*d))
462            .map(|d| {
463                let mut base = Vec3::default();
464                let tmp: Vec<_> = cmap
465                    .orbit(OrbitPolicy::Custom(&[1]), *d as DartIdType)
466                    .chain([*d])
467                    .collect();
468                tmp.windows(2).for_each(|sl| {
469                    let [d1, d2] = sl else { unreachable!() };
470                    let (vid1, vid2) = (
471                        index_map[&cmap.vertex_id(*d1)],
472                        index_map[&cmap.vertex_id(*d2)],
473                    );
474                    let (v1, v2) = (vertex_vals[vid1], vertex_vals[vid2]);
475                    base.x += (v1.y - v2.y) * (v1.z + v2.z);
476                    base.y += (v1.z - v2.z) * (v1.x + v2.x);
477                    base.z += (v1.x - v2.x) * (v1.y + v2.y);
478                });
479                (cmap.face_id(*d), base.normalize())
480            })
481            .collect();
482
483        // maps are cool => in the vertex/volume suborbit, there is exactly a single dart belonging
484        // to each intersecting faces.
485        for d in darts {
486            let fid = cmap.face_id(d);
487            let vid = index_map[&cmap.vertex_id(d)];
488            if let Some(val) = volume_normals.get_mut(&(vol, vid)) {
489                *val += norms[&fid];
490            } else {
491                volume_normals.insert((vol, vid), norms[&fid]);
492            }
493        }
494    }
495    for val in volume_normals.values_mut() {
496        *val = val.normalize();
497    }
498
499    commands.insert_resource(MapVertices(vertex_vals));
500    commands.insert_resource(FaceNormals(face_normals));
501    commands.insert_resource(VolumeNormals(volume_normals));
502
503    for bundle in darts {
504        commands.spawn(bundle);
505    }
506    for bundle in vertices {
507        commands.spawn(bundle);
508    }
509    for bundle in edges {
510        commands.spawn(bundle);
511    }
512    for bundle in faces {
513        commands.spawn(bundle);
514    }
515}