maplibre/sdf/
collision_system.rs

1use std::borrow::Cow;
2
3use cgmath::{Matrix3, Vector3};
4
5use crate::{
6    context::MapContext,
7    coords::{EXTENT, TILE_SIZE},
8    euclid::Point2D,
9    legacy::{
10        buckets::symbol_bucket::PlacedSymbol,
11        collision_feature::{CollisionBox, CollisionFeature},
12        collision_index::CollisionIndex,
13        geometry::feature_index::{IndexedSubfeature, RefIndexedSubfeature},
14        geometry_tile_data::GeometryCoordinates,
15        MapMode,
16    },
17    render::{
18        eventually::{Eventually, Eventually::Initialized},
19        shaders::SDFShaderFeatureMetadata,
20        tile_view_pattern::WgpuTileViewPattern,
21        Renderer,
22    },
23    sdf::{SymbolBufferPool, SymbolLayersDataComponent},
24    tcs::system::{System, SystemError, SystemResult},
25};
26
27pub struct CollisionSystem {}
28
29impl Default for CollisionSystem {
30    fn default() -> Self {
31        Self::new()
32    }
33}
34
35impl CollisionSystem {
36    pub fn new() -> Self {
37        Self {}
38    }
39}
40
41impl System for CollisionSystem {
42    fn name(&self) -> Cow<'static, str> {
43        "sdf_populate_world_system".into()
44    }
45
46    fn run(
47        &mut self,
48        MapContext {
49            world,
50            view_state,
51            renderer: Renderer { device, queue, .. },
52            ..
53        }: &mut MapContext,
54    ) -> SystemResult {
55        let Some((Initialized(tile_view_pattern), Initialized(symbol_buffer_pool))) =
56            world.resources.query_mut::<(
57                &mut Eventually<WgpuTileViewPattern>,
58                &mut Eventually<SymbolBufferPool>,
59            )>()
60        else {
61            return Err(SystemError::Dependencies);
62        };
63
64        if !view_state.did_camera_change() {
65            // TODO
66            // return Ok(());
67        }
68
69        let mut collision_index = CollisionIndex::new(view_state, MapMode::Continuous);
70
71        for view_tile in tile_view_pattern.iter() {
72            let coords = view_tile.coords();
73            if let Some(component) = world.tiles.query::<&SymbolLayersDataComponent>(coords) {
74                for layer in &component.layers {
75                    let metadata_count = if layer.features.is_empty() {
76                        layer.new_buffer.buffer.vertices.len()
77                    } else {
78                        layer
79                            .features
80                            .last()
81                            .map(|feature| feature.indices.end)
82                            .unwrap_or_default()
83                    };
84                    let mut feature_metadata =
85                        vec![SDFShaderFeatureMetadata { opacity: 1.0 }; metadata_count];
86
87                    for feature in &layer.features {
88                        // calculate where tile is
89
90                        let transform = coords.transform_for_zoom(view_state.zoom());
91
92                        let pos_matrix = view_state
93                            .view_projection()
94                            .to_model_view_projection(transform);
95
96                        let zoom_factor = view_state.zoom().scale_to_tile(&coords);
97
98                        let font_scale = 6.0;
99                        let scaling = Matrix3::from_cols(
100                            Vector3::new(zoom_factor * font_scale, 0.0, 0.0),
101                            Vector3::new(0.0, zoom_factor * font_scale, 0.0),
102                            Vector3::new(0.0, 0.0, 1.0),
103                        );
104
105                        let vec3 = Vector3::new(
106                            feature.bbox.max.x as f64,
107                            feature.bbox.max.y as f64,
108                            0.0f64,
109                        );
110                        let text_anchor = Vector3::new(
111                            feature.text_anchor.x as f64,
112                            feature.text_anchor.y as f64,
113                            0.0f64,
114                        );
115
116                        let shader = pos_matrix.get()
117                            * (scaling * (vec3 - text_anchor) + text_anchor).extend(1.0);
118                        let window = view_state.clip_to_window(&shader);
119
120                        //println!("{:?}", window);
121
122                        let anchor_point =
123                            Point2D::new(feature.bbox.min.x as f64, feature.bbox.min.y as f64); // TODO
124
125                        let boxes = vec![CollisionBox {
126                            anchor: anchor_point,
127                            x1: 0.0 * (EXTENT / TILE_SIZE),
128                            y1: 0. * (EXTENT / TILE_SIZE),
129                            x2: (feature.bbox.max.x - feature.bbox.min.x) as f64, //* (EXTENT / TILE_SIZE),
130                            y2: (feature.bbox.max.y - feature.bbox.min.y) as f64, // * (EXTENT / TILE_SIZE),
131                            signed_distance_from_anchor: 0.0,
132                        }]; // TODO
133
134                        let mut projected_boxes = vec![];
135                        let collision_feature = CollisionFeature {
136                            boxes,
137                            indexed_feature: IndexedSubfeature {
138                                ref_: RefIndexedSubfeature {
139                                    index: 0,
140                                    sort_index: 0,
141                                    source_layer_name: "".to_string(),
142                                    bucket_leader_id: "".to_string(),
143                                    bucket_instance_id: 0,
144                                    collision_group_id: 0,
145                                },
146                                source_layer_name_copy: "".to_string(),
147                                bucket_leader_idcopy: "".to_string(),
148                            },
149                            along_line: false, // false if point, else true
150                        };
151                        let (placed_text, is_offscreen) = collision_index.place_feature(
152                            &collision_feature,
153                            Point2D::zero(), // shift
154                            &pos_matrix,
155                            &pos_matrix.get(), // TODO
156                            //TILE_SIZE / EXTENT,
157                            1.0,
158                            &PlacedSymbol {
159                                anchor_point,
160                                segment: 0,
161                                lower_size: 0.0,
162                                upper_size: 0.0,
163                                line_offset: [0., 0.],
164                                writing_modes: Default::default(),
165                                line: GeometryCoordinates(vec![anchor_point.cast()]), // TODO can be linestring or just a single point
166                                tile_distances: vec![],                               // TODO
167                                glyph_offsets: vec![0., 0.],                          // TODO
168                                hidden: false,
169                                vertex_start_index: 0,
170                                cross_tile_id: 0,
171                                placed_orientation: None,
172                                angle: 0.0,
173
174                                placed_icon_index: None,
175                            },
176                            view_state.zoom().scale_to_zoom_level(coords.z),
177                            6.0,
178                            false,
179                            false,
180                            false,
181                            None,                               // avoidEdges
182                            Some(|f: &IndexedSubfeature| true), // collisionGroupPredicate
183                            &mut projected_boxes,               // output
184                        );
185                        if feature.str.starts_with("Ette") {
186                            //println!("{}", feature.str);
187                            //println!("{:?}", &collision_feature.boxes);
188                            //println!("proj {:?}", &projected_boxes.get(0));
189                        }
190
191                        if placed_text {
192                            collision_index.insert_feature(
193                                collision_feature,
194                                &projected_boxes,
195                                false,
196                                55,
197                                66,
198                            );
199
200                            for index in feature.indices.clone() {
201                                let index = layer.new_buffer.buffer.indices[index] as usize;
202                                feature_metadata[index].opacity = 1.0;
203                            }
204                        } else {
205                            for index in feature.indices.clone() {
206                                let index = layer.new_buffer.buffer.indices[index] as usize;
207                                feature_metadata[index].opacity = 0.0;
208                            }
209
210                            //feature_metadata.extend(iter::repeat(SDFShaderFeatureMetadata { opacity: 0.0 }).take(feature.indices.len()))
211                        }
212                    }
213
214                    if let Some(layer_at_coords) = symbol_buffer_pool.index().get_layers(coords) {
215                        for entry in layer_at_coords {
216                            debug_assert_eq!(entry.coords, coords);
217
218                            let source_layer = entry.style_layer.source_layer.as_ref().unwrap();
219
220                            if source_layer != &layer.source_layer {
221                                continue;
222                            }
223
224                            symbol_buffer_pool.update_feature_metadata(
225                                queue,
226                                entry,
227                                &feature_metadata,
228                            );
229                        }
230                    }
231                }
232            }
233        }
234        Ok(())
235    }
236}