1use std::f64::consts::PI;
4
5use cgmath::{Matrix4, Vector4};
6
7use crate::{
8 euclid::Point2D,
9 legacy::{
10 buckets::symbol_bucket::PlacedSymbol,
11 geometry_tile_data::GeometryCoordinates,
12 util::math::{convert_point_f64, perp},
13 TileSpace,
14 },
15};
16
17type PointAndCameraDistance = (Point2D<f64, TileSpace>, f64); pub struct TileDistance {
22 pub prev_tile_distance: f64,
23 pub last_segment_viewport_distance: f64,
24}
25
26pub fn project(point: Point2D<f64, TileSpace>, matrix: &Matrix4<f64>) -> PointAndCameraDistance {
28 let pos = Vector4::new(point.x, point.y, 0., 1.);
29 let pos = matrix * pos; (Point2D::new(pos[0] / pos[3], pos[1] / pos[3]), pos[3])
31}
32
33pub struct PlacedGlyph {
35 pub point: Point2D<f64, TileSpace>,
36 pub angle: f64,
37 pub tile_distance: Option<TileDistance>,
38}
39
40pub fn place_first_and_last_glyph(
42 font_scale: f64,
43 line_offset_x: f64,
44 line_offset_y: f64,
45 flip: bool,
46 anchor_point: Point2D<f64, TileSpace>,
47 tile_anchor_point: Point2D<f64, TileSpace>,
48 symbol: &PlacedSymbol,
49 label_plane_matrix: &Matrix4<f64>,
50 return_tile_distance: bool,
51) -> Option<(PlacedGlyph, PlacedGlyph)> {
52 if symbol.glyph_offsets.is_empty() {
53 assert!(false);
54 return None;
55 }
56
57 let first_glyph_offset = *symbol.glyph_offsets.first().unwrap();
58 let last_glyph_offset = *symbol.glyph_offsets.last().unwrap();
59
60 if let (Some(first_placed_glyph), Some(last_placed_glyph)) = (
61 place_glyph_along_line(
62 font_scale * first_glyph_offset,
63 line_offset_x,
64 line_offset_y,
65 flip,
66 &anchor_point,
67 &tile_anchor_point,
68 symbol.segment as i16,
69 &symbol.line,
70 &symbol.tile_distances,
71 label_plane_matrix,
72 return_tile_distance,
73 ),
74 place_glyph_along_line(
75 font_scale * last_glyph_offset,
76 line_offset_x,
77 line_offset_y,
78 flip,
79 &anchor_point,
80 &tile_anchor_point,
81 symbol.segment as i16,
82 &symbol.line,
83 &symbol.tile_distances,
84 label_plane_matrix,
85 return_tile_distance,
86 ),
87 ) {
88 return Some((first_placed_glyph, last_placed_glyph));
89 }
90
91 None
92}
93
94fn place_glyph_along_line(
96 offset_x: f64,
97 line_offset_x: f64,
98 line_offset_y: f64,
99 flip: bool,
100 projected_anchor_point: &Point2D<f64, TileSpace>,
101 tile_anchor_point: &Point2D<f64, TileSpace>,
102 anchor_segment: i16,
103 line: &GeometryCoordinates,
104 tile_distances: &Vec<f64>,
105 label_plane_matrix: &Matrix4<f64>,
106 return_tile_distance: bool,
107) -> Option<PlacedGlyph> {
108 let combined_offset_x = if flip {
109 offset_x - line_offset_x
110 } else {
111 offset_x + line_offset_x
112 };
113
114 let mut dir: i16 = if combined_offset_x > 0. { 1 } else { -1 };
115
116 let mut angle = 0.0;
117 if flip {
118 dir *= -1;
121 angle = PI;
122 }
123
124 if dir < 0 {
125 angle += PI;
126 }
127
128 let mut current_index = if dir > 0 {
129 anchor_segment
130 } else {
131 anchor_segment + 1
132 };
133
134 let initial_index = current_index;
135 let mut current = *projected_anchor_point;
136 let mut prev = *projected_anchor_point;
137 let mut distance_to_prev = 0.0;
138 let mut current_segment_distance = 0.0;
139 let abs_offset_x = combined_offset_x.abs();
140
141 while distance_to_prev + current_segment_distance <= abs_offset_x {
142 current_index += dir;
143
144 if current_index < 0 || current_index >= line.len() as i16 {
146 return None;
147 }
148
149 prev = current;
150 let projection = project(
151 convert_point_f64(&line[current_index as usize]),
152 label_plane_matrix,
153 );
154 if projection.1 > 0. {
155 current = projection.0;
156 } else {
157 let previous_tile_point = if distance_to_prev == 0. {
160 *tile_anchor_point
161 } else {
162 convert_point_f64(&line[(current_index - dir) as usize])
163 };
164
165 let current_tile_point = convert_point_f64(&line[current_index as usize]);
166 current = project_truncated_line_segment(
167 &previous_tile_point,
168 ¤t_tile_point,
169 &prev,
170 abs_offset_x - distance_to_prev + 1.,
171 label_plane_matrix,
172 );
173 }
174
175 distance_to_prev += current_segment_distance;
176 current_segment_distance = prev.distance_to(current); }
178
179 let segment_interpolation_t = (abs_offset_x - distance_to_prev) / current_segment_distance;
181 let prev_to_current = current - prev;
182 let mut p = prev + (prev_to_current * segment_interpolation_t);
183
184 p += perp(&prev_to_current) * (line_offset_y * dir as f64 / prev_to_current.length()); let segment_angle = angle + (current.y - prev.y).atan2(current.x - prev.x); Some(PlacedGlyph {
190 point: p,
191 angle: segment_angle,
192 tile_distance: if return_tile_distance {
193 Some(TileDistance {
194 prev_tile_distance: if (current_index - dir) == initial_index {
196 0.
197 } else {
198 tile_distances[(current_index - dir) as usize]
199 },
200 last_segment_viewport_distance: abs_offset_x - distance_to_prev,
201 })
202 } else {
203 None
204 },
205 })
206}
207
208fn project_truncated_line_segment(
210 &previous_tile_point: &Point2D<f64, TileSpace>,
211 current_tile_point: &Point2D<f64, TileSpace>,
212 previous_projected_point: &Point2D<f64, TileSpace>,
213 minimum_length: f64,
214 projection_matrix: &Matrix4<f64>,
215) -> Point2D<f64, TileSpace> {
216 let vec = previous_tile_point - *current_tile_point;
222 let projected_unit_vertex = project(
223 previous_tile_point + vec.try_normalize().unwrap_or(vec),
224 projection_matrix,
225 )
226 .0;
227 let projected_unit_segment = *previous_projected_point - projected_unit_vertex;
228
229 *previous_projected_point
230 + (projected_unit_segment * (minimum_length / projected_unit_segment.length()))
231 }