honeycomb_render/
import_map.rs

1use bevy::prelude::*;
2use bevy::utils::HashMap;
3use honeycomb_core::{
4    cmap::{CMap2, DartIdType, EdgeIdType, FaceIdType, OrbitPolicy, VertexIdType, VolumeIdType},
5    geometry::CoordsFloat,
6};
7
8// --- shared data
9
10/// Combinatorial map to render.
11#[derive(Resource)]
12pub struct Map<T: CoordsFloat>(pub CMap2<T>);
13
14/// Collection of a map's vertices.
15#[derive(Resource)]
16pub struct MapVertices(pub Vec<Vec3>);
17
18/// Collection of normals, organized per faces of a map.
19#[derive(Resource)]
20pub struct FaceNormals(pub HashMap<(FaceIdType, usize), Vec3>);
21
22/// Collection of normals, organized per volumes of a map.
23#[derive(Resource)]
24pub struct VolumeNormals(pub HashMap<(VolumeIdType, usize), Vec3>);
25
26// --- bundles
27
28/// Bundle used to create entities modeling dart bodies.
29#[derive(Bundle, Clone)]
30pub struct DartBundle {
31    pub(crate) id: DartId,
32    pub(crate) vertex_id: VertexId,
33    pub(crate) edge_id: EdgeId,
34    pub(crate) face_id: FaceId,
35    pub(crate) dart: Dart,
36}
37
38/// Bundle used to create entities modeling vertices.
39#[derive(Bundle, Clone)]
40pub struct VertexBundle {
41    pub(crate) id: VertexId,
42    pub(crate) vertex: Vertex,
43}
44
45/// Bundle used to create entities modeling edges.
46#[derive(Bundle, Clone)]
47pub struct EdgeBundle {
48    pub(crate) id: EdgeId,
49    pub(crate) edge: Edge,
50}
51
52/// Bundle used to create entities modeling faces.
53#[derive(Bundle, Clone)]
54pub struct FaceBundle {
55    pub(crate) id: FaceId,
56    pub(crate) face: Face,
57}
58
59// --- individual components
60
61/// Dart ID component.
62#[derive(Component, Clone)]
63pub struct DartId(pub DartIdType);
64
65/// Vertex ID component.
66#[derive(Component, Clone)]
67pub struct VertexId(pub VertexIdType);
68
69/// Edge ID component.
70#[derive(Component, Clone)]
71pub struct EdgeId(pub EdgeIdType);
72
73/// Face ID component.
74#[derive(Component, Clone)]
75pub struct FaceId(pub FaceIdType);
76
77/// Volume ID component.
78#[derive(Component, Clone)]
79pub struct VolumeId(pub VolumeIdType);
80
81/// Dart head component.
82#[derive(Component, Clone)]
83pub struct Dart {
84    pub(crate) start: usize,
85    pub(crate) end: usize,
86}
87
88/// Beta component.
89#[derive(Component, Clone)]
90pub struct Beta(pub u8, pub usize, pub usize); // beta id, v0_id, v1_id ?
91
92/// Vertex component.
93#[derive(Component, Clone)]
94pub struct Vertex(pub usize); // map id, vid
95
96/// Edge component.
97#[derive(Component, Clone)]
98pub struct Edge(pub usize, pub usize); // v0_id, v1_id
99
100/// Face component.
101#[derive(Component, Clone)]
102pub struct Face(pub Vec<usize>); // vertex list
103
104/// Volume component.
105#[derive(Component, Clone)]
106pub struct Volume;
107
108// --- startup routine
109
110/// Build ECS data from a combinatorial map object.
111///
112/// # Panics
113///
114/// This function will panic if there is a topological vertex with no associated coordinates.
115#[allow(clippy::too_many_lines)]
116pub fn extract_data_from_map<T: CoordsFloat>(mut commands: Commands, cmap: Res<Map<T>>) {
117    let cmap = &cmap.0;
118    let map_vertices: Vec<_> = cmap.iter_vertices().collect();
119    let map_edges: Vec<_> = cmap.iter_edges().collect();
120    let map_faces: Vec<_> = cmap.iter_faces().collect();
121
122    let mut index_map: HashMap<VertexIdType, usize> = HashMap::with_capacity(cmap.n_vertices());
123
124    let vertex_vals: Vec<Vec3> = map_vertices
125        .iter()
126        .enumerate()
127        .map(|(idx, vid)| {
128            index_map.insert(*vid, idx);
129            let v = cmap
130                .force_read_vertex(*vid)
131                .expect("E: found a topological vertex with no associated coordinates");
132            // sane unwraps; will crash if the coordinates cannot be converted to f32
133            Vec3::from((v.0.to_f32().unwrap(), v.1.to_f32().unwrap(), 0.0))
134        })
135        .collect();
136
137    let vertices: Vec<VertexBundle> = map_vertices
138        .iter()
139        .map(|id| VertexBundle {
140            id: VertexId(*id),
141            vertex: Vertex(index_map[id]),
142        })
143        .collect();
144
145    let edges: Vec<EdgeBundle> = map_edges
146        .iter()
147        .map(|id| {
148            let v1id = cmap.vertex_id(*id as DartIdType);
149            let v2id = if cmap.is_i_free::<2>(*id as DartIdType) {
150                cmap.vertex_id(cmap.beta::<1>(*id as DartIdType))
151            } else {
152                cmap.vertex_id(cmap.beta::<2>(*id as DartIdType))
153            };
154            EdgeBundle {
155                id: EdgeId(*id),
156                edge: Edge(index_map[&v1id], index_map[&v2id]),
157            }
158        })
159        .collect();
160
161    let mut face_normals = HashMap::new();
162    let mut darts: Vec<DartBundle> = Vec::new();
163
164    let faces: Vec<FaceBundle> = map_faces
165        .iter()
166        .map(|id| {
167            let vertex_ids: Vec<usize> = cmap
168                .orbit(OrbitPolicy::Custom(&[1]), *id as DartIdType)
169                .map(|dart_id| index_map[&cmap.vertex_id(dart_id)])
170                .collect();
171            let n_v = vertex_ids.len();
172            {
173                let (ver_in, ver, ver_out) = (&vertex_ids[n_v - 1], &vertex_ids[0], &vertex_ids[1]);
174                let (vec_in, vec_out) = (
175                    vertex_vals[*ver] - vertex_vals[*ver_in],
176                    vertex_vals[*ver_out] - vertex_vals[*ver],
177                );
178                // vec_in/out belong to X/Y plane => .cross(Z) == normal in the plane
179                // a first normalization is required because both edges should weight equally
180                let normal = (vec_in.cross(Vec3::Z).normalize()
181                    + vec_out.cross(Vec3::Z).normalize())
182                .normalize();
183                face_normals.insert((*id, *ver), normal);
184            }
185            vertex_ids.windows(3).for_each(|wdw| {
186                let [ver_in, ver, ver_out] = wdw else {
187                    unreachable!("i kneel");
188                };
189                let (vec_in, vec_out) = (
190                    vertex_vals[*ver] - vertex_vals[*ver_in],
191                    vertex_vals[*ver_out] - vertex_vals[*ver],
192                );
193                let normal = (vec_in.cross(Vec3::Z).normalize()
194                    + vec_out.cross(Vec3::Z).normalize())
195                .normalize();
196                face_normals.insert((*id, *ver), normal);
197            });
198            {
199                let (ver_in, ver, ver_out) =
200                    (&vertex_ids[n_v - 2], &vertex_ids[n_v - 1], &vertex_ids[0]);
201                let (vec_in, vec_out) = (
202                    vertex_vals[*ver] - vertex_vals[*ver_in],
203                    vertex_vals[*ver_out] - vertex_vals[*ver],
204                );
205                let normal = (vec_in.cross(Vec3::Z).normalize()
206                    + vec_out.cross(Vec3::Z).normalize())
207                .normalize();
208                face_normals.insert((*id, *ver), normal);
209            }
210
211            // common dart iterator
212            let mut tmp = cmap
213                .orbit(OrbitPolicy::Custom(&[1]), *id as DartIdType)
214                .map(|dart_id| (dart_id, index_map[&cmap.vertex_id(dart_id)]))
215                .collect::<Vec<_>>();
216            tmp.push(tmp[0]); // trick for the `.windows` call
217
218            // dart bodies
219            let bodies = tmp.windows(2).map(|wd| {
220                let [(dart_id, v1_id), (_, v2_id)] = wd else {
221                    unreachable!("i kneel");
222                };
223
224                DartBundle {
225                    id: DartId(*dart_id),
226                    vertex_id: VertexId(cmap.vertex_id(*dart_id)),
227                    edge_id: EdgeId(cmap.edge_id(*dart_id)),
228                    face_id: FaceId(*id),
229                    dart: Dart {
230                        start: *v1_id,
231                        end: *v2_id,
232                    },
233                }
234            });
235
236            darts.extend(bodies);
237
238            FaceBundle {
239                id: FaceId(*id),
240                face: Face(vertex_ids),
241            }
242        })
243        .collect();
244
245    commands.insert_resource(MapVertices(vertex_vals));
246    commands.insert_resource(FaceNormals(face_normals));
247
248    for bundle in darts {
249        commands.spawn(bundle);
250    }
251    for bundle in vertices {
252        commands.spawn(bundle);
253    }
254    for bundle in edges {
255        commands.spawn(bundle);
256    }
257    for bundle in faces {
258        commands.spawn(bundle);
259    }
260}