1use std::{
4 collections::{BTreeMap, BTreeSet, HashMap},
5 f64::consts::PI,
6 ops::Range,
7 rc::Rc,
8};
9
10use lyon::geom::euclid::Point2D;
11use widestring::U16String;
12
13use crate::{
14 coords::{EXTENT, TILE_SIZE},
15 legacy::{
16 bidi::{apply_arabic_shaping, BiDi, Char16},
17 buckets::symbol_bucket::{
18 DynamicVertex, OpacityVertex, PlacedSymbol, Segment, SymbolBucket, SymbolBucketBuffer,
19 SymbolVertex,
20 },
21 geometry::{
22 anchor::{Anchor, Anchors},
23 feature_index::{IndexedSubfeature, RefIndexedSubfeature},
24 },
25 geometry_tile_data::{FeatureType, GeometryCoordinates, SymbolGeometryTileLayer},
26 glyph::{GlyphIDs, GlyphMap, Shaping, WritingModeType},
27 glyph_atlas::GlyphPositions,
28 image::{ImageMap, ImageType},
29 image_atlas::ImagePositions,
30 layout::{
31 layout::{BucketParameters, LayoutParameters},
32 symbol_feature::SymbolGeometryTileFeature,
33 symbol_instance::{
34 ShapedTextOrientations, SymbolContent, SymbolInstance, SymbolInstanceSharedData,
35 },
36 },
37 quads::{SymbolQuad, SymbolQuads},
38 shaping::{get_anchor_justification, get_shaping, PositionedIcon},
39 style_types::*,
40 tagged_string::{SectionOptions, TaggedString},
41 util::{constants::ONE_EM, i18n, lower_bound, math::deg2radf},
42 CanonicalTileID, MapMode,
43 },
44};
45
46#[derive(Clone, Debug)]
49pub struct SymbolLayer {
50 pub layout: SymbolLayoutProperties_Unevaluated,
51}
52pub type SymbolLayer_Impl = SymbolLayer;
54#[derive(Clone, Debug)]
57pub struct LayerProperties {
58 pub id: String,
59 pub layer: SymbolLayer_Impl,
60}
61pub type SymbolLayerProperties = LayerProperties;
63impl LayerProperties {
64 pub fn layer_impl(&self) -> &SymbolLayer_Impl {
66 &self.layer
68 }
69
70 pub fn base_impl(&self) -> &Self {
72 self
73 }
74}
75pub type Bucket = SymbolBucket;
77
78#[derive(Debug)]
80pub struct LayerRenderData {
81 pub bucket: Bucket,
82 pub layer_properties: LayerProperties,
83}
84
85#[derive(Clone, Copy, Debug)]
87pub struct SortKeyRange {
88 sort_key: f64,
89 start: usize,
90 end: usize,
91}
92
93impl SortKeyRange {
94 pub fn is_first_range(&self) -> bool {
96 self.start == 0
97 }
98}
99
100pub struct FeatureIndex;
103
104fn section_options_to_value(options: &SectionOptions) -> expression::Value {
106 let mut result: HashMap<String, expression::Value> = Default::default();
107 if let Some(text_color) = &(options.text_color) {
113 result.insert(
114 expression::K_FORMATTED_SECTION_TEXT_COLOR.to_string(),
115 expression::Value::Color(text_color.clone()),
116 );
117 }
118 expression::Value::Object(result)
119}
120
121fn to_symbol_layer_properties(layer: &LayerProperties) -> &SymbolLayerProperties {
123 layer
126}
127
128fn create_layout(
130 unevaluated: &SymbolLayoutProperties_Unevaluated,
131 zoom: f64,
132) -> SymbolLayoutProperties_PossiblyEvaluated {
133 let mut layout = unevaluated.evaluate(PropertyEvaluationParameters(zoom));
134
135 if layout.get::<IconRotationAlignment>() == AlignmentType::Auto {
136 if layout.get::<SymbolPlacement>() != SymbolPlacementType::Point {
137 layout.set::<IconRotationAlignment>(AlignmentType::Map);
138 } else {
139 layout.set::<IconRotationAlignment>(AlignmentType::Viewport);
140 }
141 }
142
143 if layout.get::<TextRotationAlignment>() == AlignmentType::Auto {
144 if layout.get::<SymbolPlacement>() != SymbolPlacementType::Point {
145 layout.set::<TextRotationAlignment>(AlignmentType::Map);
146 } else {
147 layout.set::<TextRotationAlignment>(AlignmentType::Viewport);
148 }
149 }
150
151 if layout.get::<TextPitchAlignment>() == AlignmentType::Auto {
153 layout.set::<TextPitchAlignment>(layout.get::<TextRotationAlignment>());
154 }
155 if layout.get::<IconPitchAlignment>() == AlignmentType::Auto {
156 layout.set::<IconPitchAlignment>(layout.get::<IconRotationAlignment>());
157 }
158
159 layout
160}
161
162const BASELINE_OFFSET: f64 = 7.0;
168
169fn get_default_horizontal_shaping(shaped_text_orientations: &ShapedTextOrientations) -> &Shaping {
173 if shaped_text_orientations.right().is_any_line_not_empty() {
174 return shaped_text_orientations.right();
175 }
176 if shaped_text_orientations.center().is_any_line_not_empty() {
177 return shaped_text_orientations.center();
178 }
179 if shaped_text_orientations.left().is_any_line_not_empty() {
180 return shaped_text_orientations.left();
181 }
182 return shaped_text_orientations.horizontal();
183}
184
185fn shaping_for_text_justify_type(
187 shaped_text_orientations: &ShapedTextOrientations,
188 type_: TextJustifyType,
189) -> &Shaping {
190 match type_ {
191 TextJustifyType::Right => {
192 return shaped_text_orientations.right();
193 }
194
195 TextJustifyType::Left => {
196 return shaped_text_orientations.left();
197 }
198
199 TextJustifyType::Center => {
200 return shaped_text_orientations.center();
201 }
202 _ => {
203 assert!(false);
204 return shaped_text_orientations.horizontal();
205 }
206 }
207}
208
209fn evaluate_radial_offset(anchor: SymbolAnchorType, mut radial_offset: f64) -> [f64; 2] {
211 let mut result = [0.0, 0.0];
212 if radial_offset < 0.0 {
213 radial_offset = 0.0; }
215 let sqrt2 = 1.41421356237;
217 let hypotenuse = radial_offset / sqrt2;
218
219 match anchor {
220 SymbolAnchorType::TopRight | SymbolAnchorType::TopLeft => {
221 result[1] = hypotenuse - BASELINE_OFFSET;
222 }
223
224 SymbolAnchorType::BottomRight | SymbolAnchorType::BottomLeft => {
225 result[1] = -hypotenuse + BASELINE_OFFSET;
226 }
227 SymbolAnchorType::Bottom => {
228 result[1] = -radial_offset + BASELINE_OFFSET;
229 }
230 SymbolAnchorType::Top => {
231 result[1] = radial_offset - BASELINE_OFFSET;
232 }
233
234 _ => {}
235 }
236
237 match anchor {
238 SymbolAnchorType::TopRight | SymbolAnchorType::BottomRight => {
239 result[0] = -hypotenuse;
240 }
241 SymbolAnchorType::TopLeft | SymbolAnchorType::BottomLeft => {
242 result[0] = hypotenuse;
243 }
244 SymbolAnchorType::Left => {
245 result[0] = radial_offset;
246 }
247 SymbolAnchorType::Right => {
248 result[0] = -radial_offset;
249 }
250
251 _ => {}
252 }
253
254 result
255}
256
257pub struct SymbolLayout {
259 pub layer_paint_properties: BTreeMap<String, LayerProperties>,
260 pub bucket_leader_id: String,
261 pub symbol_instances: Vec<SymbolInstance>,
262 pub sort_key_ranges: Vec<SortKeyRange>,
263
264 source_layer: Box<SymbolGeometryTileLayer>,
267 overscaling: f64,
268 zoom: f64,
269 canonical_id: CanonicalTileID,
270 mode: MapMode,
271 pixel_ratio: f64,
272
273 tile_size: u32,
274 tile_pixel_ratio: f64,
275
276 icons_need_linear: bool,
277 sort_features_by_y: bool,
278 sort_features_by_key: bool,
279 allow_vertical_placement: bool,
280 icons_in_text: bool,
281 placement_modes: Vec<TextWritingModeType>,
282
283 text_size: <TextSize as DataDrivenLayoutProperty>::UnevaluatedType,
284 icon_size: <IconSize as DataDrivenLayoutProperty>::UnevaluatedType,
285 text_radial_offset: <TextRadialOffset as DataDrivenLayoutProperty>::UnevaluatedType,
286 layout: SymbolLayoutProperties_PossiblyEvaluated,
287 features: Vec<SymbolGeometryTileFeature>,
288
289 bidi: BiDi, compare_text: BTreeMap<U16String, Vec<Anchor>>,
293}
294
295impl SymbolLayout {
296 pub fn new(
298 parameters: &BucketParameters,
299 layers: &Vec<LayerProperties>,
300 source_layer: Box<SymbolGeometryTileLayer>,
301 layout_parameters: &mut LayoutParameters, ) -> Option<Self> {
303 let overscaling = parameters.tile_id.overscale_factor() as f64;
304 let zoom = parameters.tile_id.overscaled_z as f64;
305 let tile_size = (TILE_SIZE * overscaling) as u32;
306
307 let leader: &SymbolLayer_Impl =
308 to_symbol_layer_properties(layers.first().unwrap()).layer_impl();
309
310 let mut self_ = Self {
311 bucket_leader_id: layers.first().unwrap().base_impl().id.clone(),
312
313 source_layer,
314 overscaling,
315 zoom,
316 canonical_id: parameters.tile_id.canonical,
317 mode: parameters.mode,
318 pixel_ratio: parameters.pixel_ratio,
319 tile_size: tile_size,
320 tile_pixel_ratio: EXTENT / tile_size as f64,
321 layout: create_layout(
322 &to_symbol_layer_properties(layers.first().unwrap())
323 .layer_impl()
324 .layout,
325 zoom,
326 ),
327 text_size: leader.layout.get_dynamic::<TextSize>(),
328 icon_size: leader.layout.get_dynamic::<IconSize>(),
329 text_radial_offset: leader.layout.get_dynamic::<TextRadialOffset>(),
330
331 layer_paint_properties: Default::default(),
333 symbol_instances: vec![],
334 sort_key_ranges: vec![],
335 icons_need_linear: false,
336 sort_features_by_y: false,
337 sort_features_by_key: false,
338 allow_vertical_placement: false,
339 icons_in_text: false,
340 placement_modes: vec![],
341 features: vec![],
342 bidi: BiDi,
343 compare_text: Default::default(),
344 };
345
346 let has_text = self_.layout.has::<TextField>() && self_.layout.has::<TextFont>();
347 let has_icon = self_.layout.has::<IconImage>();
348
349 if !has_text && !has_icon {
350 return None;
351 }
352
353 let has_symbol_sort_key = !leader.layout.get_dynamic::<SymbolSortKey>().is_undefined();
354 let symbol_zorder = self_.layout.get::<SymbolZOrder>();
355 self_.sort_features_by_key =
356 symbol_zorder != SymbolZOrderType::ViewportY && has_symbol_sort_key;
357 let z_order_by_viewport_y = symbol_zorder == SymbolZOrderType::ViewportY
358 || (symbol_zorder == SymbolZOrderType::Auto && !self_.sort_features_by_key);
359 self_.sort_features_by_y = z_order_by_viewport_y
360 && (self_.layout.get::<TextAllowOverlap>()
361 || self_.layout.get::<IconAllowOverlap>()
362 || self_.layout.get::<TextIgnorePlacement>()
363 || self_.layout.get::<IconIgnorePlacement>());
364 if self_.layout.get::<SymbolPlacement>() == SymbolPlacementType::Point {
365 let mut modes = self_.layout.get::<TextWritingMode>();
366
367 let mut seen: BTreeSet<TextWritingModeType> = BTreeSet::new();
370 modes = modes
371 .iter()
372 .filter(|placement_mode| {
373 self_.allow_vertical_placement = self_.allow_vertical_placement
374 || **placement_mode == TextWritingModeType::Vertical;
375 seen.insert(**placement_mode)
376 })
377 .cloned()
378 .collect();
379
380 self_.placement_modes = modes;
381 }
382
383 for layer in layers {
384 self_
385 .layer_paint_properties
386 .insert(layer.base_impl().id.clone(), layer.clone());
387 }
388
389 let feature_count = self_.source_layer.feature_count();
391 for i in 0..feature_count {
392 let feature = self_.source_layer.get_feature(i);
393
394 let mut ft: SymbolGeometryTileFeature = *feature.clone();
400
401 ft.index = i;
402
403 if has_text {
404 if ft.formatted_text.is_none() {
407 let formatted = self_.layout.evaluate4::<TextField>(
408 self_.zoom,
409 &ft,
410 layout_parameters.available_images,
411 self_.canonical_id,
412 );
413 let text_transform =
414 self_
415 .layout
416 .evaluate::<TextTransform>(self_.zoom, &ft, self_.canonical_id);
417 let base_font_stack =
418 self_
419 .layout
420 .evaluate::<TextFont>(self_.zoom, &ft, self_.canonical_id);
421
422 ft.formatted_text = Some(TaggedString::default());
423 let ft_formatted_text = ft.formatted_text.as_mut().unwrap();
424 for section in &formatted.sections {
425 if let Some(image) = §ion.image {
426 layout_parameters
427 .image_dependencies
428 .insert(image.image_id.clone(), ImageType::Icon);
429 ft_formatted_text.add_image_section(image.image_id.clone());
430 } else {
431 let mut u8string = section.text.clone();
432 if text_transform == TextTransformType::Uppercase {
433 u8string = u8string.to_uppercase();
434 } else if text_transform == TextTransformType::Lowercase {
435 u8string = u8string.to_lowercase();
436 }
437
438 ft_formatted_text.add_text_section(
440 &apply_arabic_shaping(&U16String::from(u8string.as_str())),
441 if let Some(font_scale) = section.font_scale {
442 font_scale
443 } else {
444 1.0
445 },
446 if let Some(font_stack) = §ion.font_stack {
447 font_stack.clone()
448 } else {
449 base_font_stack.clone()
450 },
451 section.text_color.clone(),
452 )
453 }
458 }
459 }
460
461 let ft_formatted_text = ft.formatted_text.as_mut().unwrap();
464 let can_verticalize_text = self_.layout.get::<TextRotationAlignment>()
465 == AlignmentType::Map
466 && self_.layout.get::<SymbolPlacement>() != SymbolPlacementType::Point
467 && ft_formatted_text.allows_vertical_writing_mode();
468
469 for j in 0..ft_formatted_text.length() {
471 let section_idx = ft_formatted_text.get_section_index(j) as usize;
472 let section = ft_formatted_text.section_at(section_idx);
473 if section.image_id.is_some() {
474 continue;
475 }
476
477 let dependencies: &mut GlyphIDs = layout_parameters
478 .glyph_dependencies
479 .entry(section.font_stack.clone())
480 .or_default();
481 let code_point: Char16 = ft_formatted_text.get_char_code_at(j);
482 dependencies.insert(code_point);
483 if can_verticalize_text
484 || (self_.allow_vertical_placement
485 && ft_formatted_text.allows_vertical_writing_mode())
486 {
487 let vertical_chr: Char16 = i18n::verticalize_punctuation(code_point);
488 if vertical_chr != 0 {
489 dependencies.insert(vertical_chr);
490 }
491 }
492 }
493 }
494
495 if has_icon {
496 ft.icon = Some(self_.layout.evaluate4::<IconImage>(
497 self_.zoom,
498 &ft,
499 layout_parameters.available_images,
500 self_.canonical_id,
501 )); layout_parameters
503 .image_dependencies
504 .insert(ft.icon.as_ref().unwrap().image_id.clone(), ImageType::Icon);
505 }
506
507 if ft.formatted_text.is_some() || ft.icon.is_some() {
508 if self_.sort_features_by_key {
509 ft.sort_key =
510 self_
511 .layout
512 .evaluate::<SymbolSortKey>(self_.zoom, &ft, self_.canonical_id);
513
514 let lower_bound = lower_bound(&self_.features, &ft);
515 self_.features.insert(lower_bound, ft);
516 } else {
517 self_.features.push(ft);
518 }
519 }
520 }
521
522 if self_.layout.get::<SymbolPlacement>() == SymbolPlacementType::Line {
523 todo!()
524 }
526
527 Some(self_)
528 }
529 pub fn prepare_symbols(
531 &mut self,
532 glyph_map: &GlyphMap,
533 glyph_positions: &GlyphPositions,
534 image_map: &ImageMap,
535 image_positions: &ImagePositions,
536 ) {
537 let is_point_placement: bool =
538 self.layout.get::<SymbolPlacement>() == SymbolPlacementType::Point;
539 let text_along_line: bool =
540 self.layout.get::<TextRotationAlignment>() == AlignmentType::Map && !is_point_placement;
541
542 let mut to_process_features = Vec::new();
543
544 for (feature_index, feature) in self.features.iter_mut().enumerate() {
545 if feature.geometry.is_empty() {
547 continue;
548 }
549
550 let mut shaped_text_orientations: ShapedTextOrientations =
551 ShapedTextOrientations::default();
552 let mut shaped_icon: Option<PositionedIcon> = None;
553 let mut text_offset = [0.0, 0.0];
554 let layout_text_size: f64 =
555 self.layout
556 .evaluate::<TextSize>(self.zoom + 1., feature, self.canonical_id);
557 let layout_text_size_at_bucket_zoom_level: f64 =
558 self.layout
559 .evaluate::<TextSize>(self.zoom, feature, self.canonical_id);
560 let layout_icon_size: f64 =
561 self.layout
562 .evaluate::<IconSize>(self.zoom + 1., feature, self.canonical_id);
563
564 if let Some(mut feature_formatted_text) = feature.formatted_text.clone() {
566 if layout_text_size > 0.0 {
567 let line_height: f64 = self.layout.get::<TextLineHeight>() * ONE_EM;
568 let spacing: f64 =
569 if i18n::allows_letter_spacing(feature_formatted_text.raw_text()) {
570 self.layout.evaluate::<TextLetterSpacing>(
571 self.zoom,
572 feature,
573 self.canonical_id,
574 ) * ONE_EM
575 } else {
576 0.0
577 };
578
579 let apply_shaping = |formatted_text: &TaggedString,
580 writing_mode: WritingModeType,
581 text_anchor: SymbolAnchorType,
582 text_justify: TextJustifyType,
583 text_offset: &[f64; 2]|
584 -> Shaping {
585 get_shaping(
586 formatted_text,
587 if is_point_placement {
589 self.layout.evaluate::<TextMaxWidth>(
590 self.zoom,
591 feature,
592 self.canonical_id,
593 ) * ONE_EM
594 } else {
595 0.0
596 },
597 line_height,
598 text_anchor,
599 text_justify,
600 spacing,
601 text_offset,
602 writing_mode,
603 &self.bidi,
604 glyph_map,
605 glyph_positions,
606 image_positions,
607 layout_text_size,
608 layout_text_size_at_bucket_zoom_level,
609 self.allow_vertical_placement,
610 )
611 };
612
613 let variable_text_anchor: Vec<TextVariableAnchorType> =
614 self.layout.evaluate_static::<TextVariableAnchor>(
615 self.zoom,
616 feature,
617 self.canonical_id,
618 );
619 let text_anchor: SymbolAnchorType =
620 self.layout
621 .evaluate::<TextAnchor>(self.zoom, feature, self.canonical_id);
622 if variable_text_anchor.is_empty() {
623 let radial_offset: f64 = self.layout.evaluate::<TextRadialOffset>(
627 self.zoom,
628 feature,
629 self.canonical_id,
630 );
631 if radial_offset > 0.0 {
632 text_offset =
637 evaluate_radial_offset(text_anchor, radial_offset * ONE_EM);
638 } else {
639 text_offset = [
640 self.layout.evaluate::<TextOffset>(
641 self.zoom,
642 feature,
643 self.canonical_id,
644 )[0] * ONE_EM,
645 self.layout.evaluate::<TextOffset>(
646 self.zoom,
647 feature,
648 self.canonical_id,
649 )[1] * ONE_EM,
650 ];
651 }
652 }
653 let mut text_justify = if text_along_line {
654 TextJustifyType::Center
655 } else {
656 self.layout
657 .evaluate::<TextJustify>(self.zoom, feature, self.canonical_id)
658 };
659
660 let add_vertical_shaping_for_point_label_if_needed =
661 |shaped_text_orientations: &mut ShapedTextOrientations,
662 feature_formatted_text: &mut TaggedString| {
663 if self.allow_vertical_placement
664 && feature_formatted_text.allows_vertical_writing_mode()
665 {
666 feature_formatted_text.verticalize_punctuation();
667 shaped_text_orientations.set_vertical(apply_shaping(
673 feature_formatted_text,
674 WritingModeType::Vertical,
675 text_anchor,
676 TextJustifyType::Left,
677 &text_offset,
678 ))
679 }
680 };
681
682 if !text_along_line && !variable_text_anchor.is_empty() {
685 let mut justifications: Vec<TextJustifyType> = Vec::new();
686 if text_justify != TextJustifyType::Auto {
687 justifications.push(text_justify);
688 } else {
689 for anchor in &variable_text_anchor {
690 justifications.push(get_anchor_justification(anchor));
691 }
692 }
693 for justification in justifications {
694 let mut shaping_for_justification = shaping_for_text_justify_type(
695 &shaped_text_orientations,
696 justification,
697 );
698 if shaping_for_justification.is_any_line_not_empty() {
699 continue;
700 }
701 let shaping = apply_shaping(
705 &feature_formatted_text,
706 WritingModeType::Horizontal,
707 SymbolAnchorType::Center,
708 justification,
709 &text_offset,
710 );
711 if shaping.is_any_line_not_empty() {
712 shaping_for_justification = &shaping;
713 if shaping_for_justification.positioned_lines.len() == 1 {
714 shaped_text_orientations.single_line = true;
715 break;
716 }
717 }
718 }
719
720 add_vertical_shaping_for_point_label_if_needed(
722 &mut shaped_text_orientations,
723 &mut feature_formatted_text,
724 );
725 } else {
726 if text_justify == TextJustifyType::Auto {
727 text_justify = get_anchor_justification(&text_anchor);
728 }
729
730 let shaping = apply_shaping(
732 &feature_formatted_text,
733 WritingModeType::Horizontal,
734 text_anchor,
735 text_justify,
736 &text_offset,
737 );
738 if shaping.is_any_line_not_empty() {
739 shaped_text_orientations.set_horizontal(shaping)
740 }
741
742 add_vertical_shaping_for_point_label_if_needed(
744 &mut shaped_text_orientations,
745 &mut feature_formatted_text,
746 );
747
748 if text_along_line && feature_formatted_text.allows_vertical_writing_mode()
750 {
751 feature_formatted_text.verticalize_punctuation();
752 shaped_text_orientations.set_vertical(apply_shaping(
753 &feature_formatted_text,
754 WritingModeType::Vertical,
755 text_anchor,
756 text_justify,
757 &text_offset,
758 ));
759 }
760 }
761 }
762
763 feature.formatted_text = Some(feature_formatted_text);
764 }
765
766 let mut icon_type: SymbolContent = SymbolContent::None;
768 if let Some(icon) = &feature.icon {
769 let image = image_map.get(&icon.image_id);
770 if let Some(image) = image {
771 icon_type = SymbolContent::IconRGBA;
772 shaped_icon = Some(PositionedIcon::shape_icon(
773 image_positions.get(&icon.image_id).unwrap().clone(),
774 &self
775 .layout
776 .evaluate::<IconOffset>(self.zoom, feature, self.canonical_id),
777 self.layout
778 .evaluate::<IconAnchor>(self.zoom, feature, self.canonical_id),
779 ));
780 if image.sdf {
781 icon_type = SymbolContent::IconSDF;
782 }
783 if image.pixel_ratio != self.pixel_ratio {
784 self.icons_need_linear = true;
785 } else if self.layout.get_dynamic::<IconRotate>().constant_or(1.0) != 0.0 {
786 self.icons_need_linear = true;
787 }
788 }
789 }
790
791 let default_shaping = get_default_horizontal_shaping(&shaped_text_orientations);
793 self.icons_in_text = if default_shaping.is_any_line_not_empty() {
794 default_shaping.icons_in_text
795 } else {
796 false
797 };
798 if default_shaping.is_any_line_not_empty() || shaped_icon.is_some() {
799 to_process_features.push((
801 feature_index,
802 shaped_text_orientations,
803 shaped_icon,
804 image_map,
805 text_offset,
806 layout_text_size,
807 layout_icon_size,
808 icon_type,
809 ));
810 }
811 }
812
813 for (
814 feature_index,
815 shaped_text_orientations,
816 shaped_icon,
817 imageMap,
818 text_offset,
819 layout_text_size,
820 layout_icon_size,
821 icon_type,
822 ) in to_process_features
823 {
824 self.add_feature(
825 feature_index,
826 &self.features[feature_index].clone(), &shaped_text_orientations,
828 shaped_icon,
829 imageMap,
830 text_offset,
831 layout_text_size,
832 layout_icon_size,
833 icon_type,
834 );
835
836 self.features[feature_index].geometry.clear();
837 }
838
839 self.compare_text.clear();
840 }
841
842 pub fn create_bucket(
844 &self,
845 _image_positions: ImagePositions,
846 _feature_index: Box<FeatureIndex>,
847 render_data: &mut HashMap<String, LayerRenderData>,
848 first_load: bool,
849 show_collision_boxes: bool,
850 canonical: &CanonicalTileID,
851 ) {
852 let mut symbol_instances = self.symbol_instances.clone(); let mut bucket: SymbolBucket = SymbolBucket::new(
854 self.layout.clone(),
855 &self.layer_paint_properties,
856 &self.text_size,
857 &self.icon_size,
858 self.zoom,
859 self.icons_need_linear,
860 self.sort_features_by_y,
861 self.bucket_leader_id.clone(),
862 self.symbol_instances.clone(), self.sort_key_ranges.clone(),
864 self.tile_pixel_ratio,
865 self.allow_vertical_placement,
866 self.placement_modes.clone(),
867 self.icons_in_text,
868 );
869
870 for symbol_instance in &mut symbol_instances {
871 let has_text = symbol_instance.has_text();
872 let has_icon = symbol_instance.has_icon();
873 let single_line = symbol_instance.single_line;
874
875 let feature = self
876 .features
877 .get(symbol_instance.layout_feature_index)
878 .unwrap();
879
880 if has_icon {
886 let size_data: Range<f64> = bucket.icon_size_binder.get_vertex_size_data(feature); let icon_buffer = if symbol_instance.has_sdf_icon() {
888 &mut bucket.sdf_icon
889 } else {
890 &mut bucket.icon
891 };
892 let mut place_icon =
893 |icon_quads: &SymbolQuads, mut index: usize, writing_mode: WritingModeType| {
894 let mut icon_symbol = PlacedSymbol {
895 anchor_point: symbol_instance.anchor.point,
896 segment: symbol_instance.anchor.segment.unwrap_or(0),
897 lower_size: size_data.start,
898 upper_size: size_data.end,
899 line_offset: symbol_instance.icon_offset,
900 writing_modes: writing_mode,
901 line: symbol_instance.line().clone(),
902 tile_distances: Vec::new(),
903 glyph_offsets: vec![],
904 hidden: false,
905 vertex_start_index: 0,
906 cross_tile_id: 0,
907 placed_orientation: None,
908 angle: if self.allow_vertical_placement
909 && writing_mode == WritingModeType::Vertical
910 {
911 PI / 2.
912 } else {
913 0.0
914 },
915 placed_icon_index: None,
916 };
917
918 icon_symbol.vertex_start_index = self.add_symbols(
919 icon_buffer,
920 size_data.clone(),
921 icon_quads,
922 &symbol_instance.anchor,
923 &mut icon_symbol,
924 feature.sort_key,
925 );
926
927 icon_buffer.placed_symbols.push(icon_symbol);
928 index = icon_buffer.placed_symbols.len() - 1; };
930
931 place_icon(
932 symbol_instance.icon_quads().as_ref().unwrap(),
933 symbol_instance.placed_icon_index.unwrap(),
934 WritingModeType::None,
935 );
936 if let Some(vertical_icon_quads) = symbol_instance.vertical_icon_quads() {
937 place_icon(
938 vertical_icon_quads,
939 symbol_instance.placed_vertical_icon_index.unwrap(),
940 WritingModeType::Vertical,
941 );
942 }
943
944 assert!(bucket.paint_properties.is_empty())
946 }
957
958 if has_text && feature.formatted_text.is_some() {
959 let mut last_added_section: Option<usize> = None;
960 if single_line {
961 let mut placed_text_index: Option<usize> = None;
962 let (new_last_added_section, new_placed_index) = self.add_symbol_glyph_quads(
963 &mut bucket,
964 symbol_instance,
965 feature,
966 symbol_instance.writing_modes,
967 placed_text_index,
968 symbol_instance.right_justified_glyph_quads(),
969 canonical,
970 last_added_section,
971 );
972 last_added_section = Some(new_last_added_section);
973 placed_text_index = new_placed_index;
974 symbol_instance.placed_right_text_index = placed_text_index;
975 symbol_instance.placed_center_text_index = placed_text_index;
976 symbol_instance.placed_left_text_index = placed_text_index;
977 } else {
978 if symbol_instance.right_justified_glyph_quads_size != 0 {
979 let (new_last_added_section, new_placed_index) = self
980 .add_symbol_glyph_quads(
981 &mut bucket,
982 symbol_instance,
983 feature,
984 symbol_instance.writing_modes,
985 symbol_instance.placed_right_text_index,
986 symbol_instance.right_justified_glyph_quads(),
987 canonical,
988 last_added_section,
989 );
990 last_added_section = Some(new_last_added_section);
991 symbol_instance.placed_right_text_index = new_placed_index
992 }
993 if symbol_instance.center_justified_glyph_quads_size != 0 {
994 let (new_last_added_section, new_placed_index) = self
995 .add_symbol_glyph_quads(
996 &mut bucket,
997 symbol_instance,
998 feature,
999 symbol_instance.writing_modes,
1000 symbol_instance.placed_center_text_index,
1001 symbol_instance.center_justified_glyph_quads(),
1002 canonical,
1003 last_added_section,
1004 );
1005 last_added_section = Some(new_last_added_section);
1006 symbol_instance.placed_center_text_index = new_placed_index
1007 }
1008 if symbol_instance.left_justified_glyph_quads_size != 0 {
1009 let (new_last_added_section, new_placed_index) = self
1010 .add_symbol_glyph_quads(
1011 &mut bucket,
1012 symbol_instance,
1013 feature,
1014 symbol_instance.writing_modes,
1015 symbol_instance.placed_left_text_index,
1016 symbol_instance.left_justified_glyph_quads(),
1017 canonical,
1018 last_added_section,
1019 );
1020 last_added_section = Some(new_last_added_section);
1021 symbol_instance.placed_left_text_index = new_placed_index
1022 }
1023 }
1024 if symbol_instance.writing_modes.contains(WritingModeType::Vertical) && symbol_instance.vertical_glyph_quads_size != 0
1026 {
1027 let (new_last_added_section, new_placed_index) = self.add_symbol_glyph_quads(
1028 &mut bucket,
1029 symbol_instance,
1030 feature,
1031 WritingModeType::Vertical,
1032 symbol_instance.placed_vertical_text_index,
1033 symbol_instance.vertical_glyph_quads(),
1034 canonical,
1035 last_added_section,
1036 );
1037 last_added_section = Some(new_last_added_section);
1038 symbol_instance.placed_vertical_text_index = new_placed_index
1039 }
1040 assert!(last_added_section.is_some()); self.update_paint_properties_for_section(
1042 &mut bucket,
1043 feature,
1044 last_added_section.unwrap(),
1045 canonical,
1046 );
1047 }
1048
1049 symbol_instance.release_shared_data();
1050 }
1051
1052 if show_collision_boxes {
1053 self.add_to_debug_buffers(&mut bucket);
1054 }
1055 if bucket.has_data() {
1056 for pair in &self.layer_paint_properties {
1057 if !first_load {
1058 bucket.just_reloaded = true;
1059 }
1060 render_data.insert(
1061 pair.0.clone(),
1062 LayerRenderData {
1063 bucket: bucket.clone(), layer_properties: pair.1.clone(),
1065 },
1066 );
1067 }
1068 }
1069 }
1070
1071 fn has_symbol_instances(&self) -> bool {
1073 !self.symbol_instances.is_empty()
1074 }
1075 fn has_dependencies(&self) -> bool {
1077 !self.features.is_empty()
1078 }
1079
1080 pub const INVALID_OFFSET_VALUE: f64 = f64::MAX;
1081 pub fn evaluate_variable_offset(anchor: SymbolAnchorType, mut offset: [f64; 2]) -> [f64; 2] {
1091 if offset[1] == Self::INVALID_OFFSET_VALUE {
1092 return evaluate_radial_offset(anchor, offset[0]);
1093 }
1094 let mut result = [0.0, 0.0];
1095 offset[0] = (offset[0]).abs();
1096 offset[1] = (offset[1]).abs();
1097
1098 match anchor {
1099 SymbolAnchorType::TopRight | SymbolAnchorType::TopLeft | SymbolAnchorType::Top => {
1100 result[1] = offset[1] - BASELINE_OFFSET;
1101 }
1102
1103 SymbolAnchorType::BottomRight
1104 | SymbolAnchorType::BottomLeft
1105 | SymbolAnchorType::Bottom => {
1106 result[1] = -offset[1] + BASELINE_OFFSET;
1107 }
1108
1109 SymbolAnchorType::Center | SymbolAnchorType::Left | SymbolAnchorType::Right => {}
1110 }
1111
1112 match anchor {
1113 SymbolAnchorType::TopRight
1114 | SymbolAnchorType::BottomRight
1115 | SymbolAnchorType::Right => {
1116 result[0] = -offset[0];
1117 }
1118 SymbolAnchorType::TopLeft | SymbolAnchorType::BottomLeft | SymbolAnchorType::Left => {
1119 result[0] = offset[0];
1120 }
1121 SymbolAnchorType::Center | SymbolAnchorType::Top | SymbolAnchorType::Bottom => {}
1122 }
1123
1124 result
1125 }
1126
1127 pub fn calculate_tile_distances(line: &GeometryCoordinates, anchor: &Anchor) -> Vec<f64> {
1132 let mut tile_distances: Vec<f64> = vec![0.0; line.len()];
1133 if let Some(segment) = anchor.segment {
1134 assert!(segment < line.len());
1135 let mut sum_forward_length = if segment + 1 < line.len() {
1136 anchor.point.distance_to(line[segment + 1].cast::<f64>())
1137 } else {
1138 0.0
1139 };
1140 let mut sum_backward_length = anchor.point.distance_to(line[segment].cast::<f64>());
1141 for i in segment + 1..line.len() {
1142 tile_distances[i] = sum_forward_length;
1143 if i < line.len() - 1 {
1144 sum_forward_length +=
1145 line[i + 1].cast::<f64>().distance_to(line[i].cast::<f64>());
1146 }
1147 }
1148
1149 let mut i = segment;
1150 loop {
1151 tile_distances[i] = sum_backward_length;
1152 if i != 0 {
1153 sum_backward_length +=
1154 line[i - 1].cast::<f64>().distance_to(line[i].cast::<f64>());
1155 } else {
1156 break; }
1158 i -= 1;
1159 }
1160 }
1161 tile_distances
1162 }
1163}
1164
1165impl SymbolLayout {
1166 fn add_feature(
1168 &mut self,
1169 layout_feature_index: usize,
1170 feature: &SymbolGeometryTileFeature,
1171 shaped_text_orientations: &ShapedTextOrientations,
1172 mut shaped_icon: Option<PositionedIcon>, image_map: &ImageMap,
1174 text_offset: [f64; 2],
1175 layout_text_size: f64,
1176 layout_icon_size: f64,
1177 icon_type: SymbolContent,
1178 ) {
1179 let min_scale = 0.5;
1180 let glyph_size = 24.0;
1181
1182 let icon_offset: [f64; 2] =
1183 self.layout
1184 .evaluate::<IconOffset>(self.zoom, feature, self.canonical_id);
1185
1186 let text_max_size = self
1191 .layout
1192 .evaluate::<TextSize>(18., feature, self.canonical_id);
1193
1194 let font_scale = layout_text_size / glyph_size;
1195 let text_box_scale = self.tile_pixel_ratio * font_scale;
1196 let text_max_box_scale = self.tile_pixel_ratio * text_max_size / glyph_size;
1197 let icon_box_scale = self.tile_pixel_ratio * layout_icon_size;
1198 let symbol_spacing = self.tile_pixel_ratio * self.layout.get::<SymbolSpacing>();
1199 let text_padding = self.layout.get::<TextPadding>() * self.tile_pixel_ratio;
1200 let icon_padding = self.layout.get::<IconPadding>() * self.tile_pixel_ratio;
1201 let text_max_angle = deg2radf(self.layout.get::<TextMaxAngle>());
1202 let icon_rotation =
1203 self.layout
1204 .evaluate::<IconRotate>(self.zoom, feature, self.canonical_id);
1205 let text_rotation =
1206 self.layout
1207 .evaluate::<TextRotate>(self.zoom, feature, self.canonical_id);
1208 let variable_text_offset: [f64; 2];
1209 if !self.text_radial_offset.is_undefined() {
1210 variable_text_offset = [
1211 self.layout
1212 .evaluate::<TextRadialOffset>(self.zoom, feature, self.canonical_id)
1213 * ONE_EM,
1214 Self::INVALID_OFFSET_VALUE,
1215 ];
1216 } else {
1217 variable_text_offset = [
1218 self.layout
1219 .evaluate::<TextOffset>(self.zoom, feature, self.canonical_id)[0]
1220 * ONE_EM,
1221 self.layout
1222 .evaluate::<TextOffset>(self.zoom, feature, self.canonical_id)[1]
1223 * ONE_EM,
1224 ];
1225 }
1226
1227 let text_placement: SymbolPlacementType =
1228 if self.layout.get::<TextRotationAlignment>() != AlignmentType::Map {
1229 SymbolPlacementType::Point
1230 } else {
1231 self.layout.get::<SymbolPlacement>()
1232 };
1233
1234 let text_repeat_distance: f64 = symbol_spacing / 2.;
1235 let evaluated_layout_properties: SymbolLayoutProperties_Evaluated =
1236 self.layout.evaluate_feature(self.zoom, feature);
1237 let indexed_feature = IndexedSubfeature {
1238 ref_: RefIndexedSubfeature {
1239 index: feature.index,
1240 sort_index: self.symbol_instances.len(),
1241 source_layer_name: self.source_layer.get_name().to_string(),
1242 bucket_leader_id: self.bucket_leader_id.clone(),
1243 bucket_instance_id: 0,
1244 collision_group_id: 0,
1245 },
1246 source_layer_name_copy: self.source_layer.get_name().to_string(),
1247 bucket_leader_idcopy: self.bucket_leader_id.clone(),
1248 };
1249
1250 let icon_text_fit = evaluated_layout_properties.get::<IconTextFit>();
1251 let has_icon_text_fit = icon_text_fit != IconTextFitType::None;
1252 let mut vertically_shaped_icon: Option<PositionedIcon> = None;
1254
1255 if let Some(shaped_icon) = &mut shaped_icon {
1256 if has_icon_text_fit {
1257 if self.allow_vertical_placement
1259 && shaped_text_orientations.vertical().is_any_line_not_empty()
1260 {
1261 vertically_shaped_icon = Some(shaped_icon.clone());
1262 vertically_shaped_icon.as_mut().unwrap().fit_icon_to_text(
1263 shaped_text_orientations.vertical(),
1264 icon_text_fit,
1265 &self.layout.get::<IconTextFitPadding>(),
1266 &icon_offset,
1267 font_scale,
1268 );
1269 }
1270 let shaped_text = get_default_horizontal_shaping(shaped_text_orientations);
1271 if shaped_text.is_any_line_not_empty() {
1272 shaped_icon.fit_icon_to_text(
1273 shaped_text,
1274 icon_text_fit,
1275 &self.layout.get::<IconTextFitPadding>(),
1276 &icon_offset,
1277 font_scale,
1278 );
1279 }
1280 }
1281 }
1282
1283 let mut add_symbol_instance =
1284 |anchor: &Anchor, shared_data: Rc<SymbolInstanceSharedData>| {
1285 let anchor_inside_tile = anchor.point.x >= 0.
1287 && anchor.point.x < EXTENT
1288 && anchor.point.y >= 0.
1289 && anchor.point.y < EXTENT;
1290
1291 if self.mode == MapMode::Tile || anchor_inside_tile {
1292 self.symbol_instances.push(SymbolInstance::new(
1298 *anchor,
1299 shared_data,
1300 shaped_text_orientations,
1301 &shaped_icon,
1302 &vertically_shaped_icon,
1303 text_box_scale,
1304 text_padding,
1305 text_placement,
1306 text_offset,
1307 icon_box_scale,
1308 icon_padding,
1309 icon_offset,
1310 indexed_feature.clone(),
1311 layout_feature_index,
1312 feature.index,
1313 if let Some(formatted_text) = &feature.formatted_text {
1314 formatted_text.raw_text().clone()
1315 } else {
1316 U16String::new()
1317 },
1318 self.overscaling,
1319 icon_rotation,
1320 text_rotation,
1321 variable_text_offset,
1322 self.allow_vertical_placement,
1323 icon_type,
1324 ));
1325
1326 if self.sort_features_by_key {
1327 if !self.sort_key_ranges.is_empty()
1328 && self.sort_key_ranges.last().unwrap().sort_key == feature.sort_key
1329 {
1330 self.sort_key_ranges.last_mut().unwrap().end =
1331 self.symbol_instances.len();
1332 } else {
1333 self.sort_key_ranges.push(SortKeyRange {
1334 sort_key: feature.sort_key,
1335 start: self.symbol_instances.len() - 1,
1336 end: self.symbol_instances.len(),
1337 });
1338 }
1339 }
1340 }
1341 };
1342
1343 let create_symbol_instance_shared_data = |line: GeometryCoordinates| {
1344 Rc::new(SymbolInstanceSharedData::new(
1345 line,
1346 shaped_text_orientations,
1347 shaped_icon.clone(),
1348 vertically_shaped_icon.clone(),
1349 &evaluated_layout_properties,
1350 text_placement,
1351 text_offset,
1352 image_map,
1353 icon_rotation,
1354 icon_type,
1355 has_icon_text_fit,
1356 self.allow_vertical_placement,
1357 ))
1358 };
1359
1360 let type_ = feature.get_type();
1361
1362 if self.layout.get::<SymbolPlacement>() == SymbolPlacementType::Line {
1363 todo!()
1364 } else if self.layout.get::<SymbolPlacement>() == SymbolPlacementType::LineCenter {
1402 todo!()
1403 } else if type_ == FeatureType::Polygon {
1434 todo!()
1435 } else if type_ == FeatureType::LineString {
1451 for line in &feature.geometry {
1452 if line.0.is_empty() {
1454 continue;
1455 }
1456
1457 let anchor = Anchor {
1458 point: Point2D::new((line[0].x) as f64, (line[0].y) as f64),
1459 angle: 0.0,
1460 segment: Some((min_scale) as usize),
1461 };
1462 add_symbol_instance(&anchor, create_symbol_instance_shared_data(line.clone()));
1463 }
1464 } else if type_ == FeatureType::Point {
1465 for points in &feature.geometry {
1466 for point in &points.0 {
1467 let anchor = Anchor {
1468 point: Point2D::new((point.x) as f64, (point.y) as f64),
1469 angle: 0.0,
1470 segment: Some((min_scale) as usize),
1471 };
1472 add_symbol_instance(
1473 &anchor,
1474 create_symbol_instance_shared_data(GeometryCoordinates(vec![*point])),
1475 );
1476 }
1477 }
1478 }
1479 }
1480
1481 fn anchor_is_too_close(
1483 &mut self,
1484 text: &U16String,
1485 repeat_distance: f64,
1486 anchor: &Anchor,
1487 ) -> bool {
1488 if let Some(other_anchors) = self.compare_text.get(text) {
1489 for other_anchor in other_anchors {
1490 if anchor.point.distance_to(other_anchor.point) < repeat_distance {
1491 return true;
1492 }
1493 }
1494 } else {
1495 self.compare_text.insert(text.clone(), Anchors::new());
1496 }
1497
1498 let anchors = self.compare_text.get_mut(text).unwrap();
1499 anchors.push(*anchor);
1500 false
1501 }
1502
1503 fn add_to_debug_buffers(&self, bucket: &mut SymbolBucket) {
1505 todo!()
1506 }
1507
1508 fn add_symbol(
1511 &self,
1512 buffer: &mut SymbolBucketBuffer,
1513 size_data: Range<f64>,
1514 symbol: &SymbolQuad,
1515 label_anchor: &Anchor,
1516 sort_key: f64,
1517 ) -> usize {
1518 let vertex_length: u16 = 4;
1519
1520 let tl = symbol.tl;
1521 let tr = symbol.tr;
1522 let bl = symbol.bl;
1523 let br = symbol.br;
1524 let tex = symbol.tex;
1525 let pixel_offset_tl = symbol.pixel_offset_tl;
1526 let pixel_offset_br = symbol.pixel_offset_br;
1527 let min_font_scale = symbol.min_font_scale;
1528
1529 if buffer.segments.is_empty()
1530 || buffer.segments.last().unwrap().vertex_length + vertex_length as usize
1531 > u16::MAX as usize
1532 || (buffer.segments.last().unwrap().sort_key - sort_key).abs() > f64::EPSILON
1533 {
1534 buffer.segments.push(Segment {
1535 vertex_offset: buffer.shared_vertices.len(),
1536 index_offset: buffer.triangles.len(),
1537 vertex_length: 0,
1538 index_length: 0,
1539 sort_key,
1540 _phandom_data: Default::default(),
1541 });
1542 }
1543
1544 let segment = buffer.segments.last_mut().unwrap();
1547 assert!(segment.vertex_length <= u16::MAX as usize);
1548 let index = (segment.vertex_length) as u16;
1549
1550 let vertices = &mut buffer.shared_vertices;
1552 vertices.push(SymbolVertex::new(
1553 label_anchor.point,
1554 tl,
1555 symbol.glyph_offset.y,
1556 tex.origin.x,
1557 tex.origin.y,
1558 size_data.clone(),
1559 symbol.is_sdf,
1560 pixel_offset_tl,
1561 min_font_scale,
1562 ));
1563 vertices.push(SymbolVertex::new(
1564 label_anchor.point,
1565 tr,
1566 symbol.glyph_offset.y,
1567 tex.origin.x + tex.width(),
1568 tex.origin.y,
1569 size_data.clone(),
1570 symbol.is_sdf,
1571 Point2D::new(pixel_offset_br.x, pixel_offset_tl.y),
1572 min_font_scale,
1573 ));
1574 vertices.push(SymbolVertex::new(
1575 label_anchor.point,
1576 bl,
1577 symbol.glyph_offset.y,
1578 tex.origin.x,
1579 tex.origin.y + tex.height(),
1580 size_data.clone(),
1581 symbol.is_sdf,
1582 Point2D::new(pixel_offset_tl.x, pixel_offset_br.y),
1583 min_font_scale,
1584 ));
1585 vertices.push(SymbolVertex::new(
1586 label_anchor.point,
1587 br,
1588 symbol.glyph_offset.y,
1589 tex.origin.x + tex.width(),
1590 tex.origin.y + tex.height(),
1591 size_data.clone(),
1592 symbol.is_sdf,
1593 pixel_offset_br,
1594 min_font_scale,
1595 ));
1596
1597 let dynamic_vertex = DynamicVertex::new(label_anchor.point, 0.);
1601 buffer.shared_dynamic_vertices.push(dynamic_vertex);
1602 buffer.shared_dynamic_vertices.push(dynamic_vertex);
1603 buffer.shared_dynamic_vertices.push(dynamic_vertex);
1604 buffer.shared_dynamic_vertices.push(dynamic_vertex);
1605
1606 let opacity_vertex = OpacityVertex::new(true, 1.0);
1607 buffer.shared_opacity_vertices.push(opacity_vertex);
1608 buffer.shared_opacity_vertices.push(opacity_vertex);
1609 buffer.shared_opacity_vertices.push(opacity_vertex);
1610 buffer.shared_opacity_vertices.push(opacity_vertex);
1611
1612 buffer.triangles.push(index, index + 1, index + 2);
1614 buffer.triangles.push(index + 1, index + 2, index + 3);
1615
1616 segment.vertex_length += vertex_length as usize;
1617 segment.index_length += 6;
1618
1619 index as usize
1620 }
1621 fn add_symbols(
1623 &self,
1624 buffer: &mut SymbolBucketBuffer,
1625 size_data: Range<f64>,
1626 symbols: &SymbolQuads,
1627 label_anchor: &Anchor,
1628 placed_symbol: &mut PlacedSymbol,
1629 sort_key: f64,
1630 ) -> usize {
1631 let mut first_symbol = true;
1632 let mut first_index = 0;
1633 for symbol in symbols {
1634 let index = self.add_symbol(buffer, size_data.clone(), symbol, label_anchor, sort_key);
1635 placed_symbol.glyph_offsets.push(symbol.glyph_offset.x);
1636 if first_symbol {
1637 first_index = index;
1638 first_symbol = false;
1639 }
1640 }
1641 first_index
1642 }
1643
1644 fn add_symbol_glyph_quads(
1648 &self,
1649 bucket: &mut SymbolBucket,
1650 symbol_instance: &SymbolInstance,
1651 feature: &SymbolGeometryTileFeature,
1652 writing_mode: WritingModeType,
1653 placed_index: Option<usize>, glyph_quads: &SymbolQuads,
1655 canonical: &CanonicalTileID,
1656 mut last_added_section: Option<usize>, ) -> (usize, Option<usize>) {
1658 let mut output_placed_index = placed_index;
1659 let size_data: Range<f64> = bucket.text_size_binder.get_vertex_size_data(feature); let has_format_section_overrides: bool = bucket.has_format_section_overrides();
1661 let placed_icon_index = if writing_mode == WritingModeType::Vertical {
1662 symbol_instance.placed_vertical_icon_index
1663 } else {
1664 symbol_instance.placed_icon_index
1665 };
1666
1667 let mut newly_placed_symbol = PlacedSymbol {
1669 anchor_point: symbol_instance.anchor.point,
1670 segment: symbol_instance.anchor.segment.unwrap_or(0),
1671 lower_size: size_data.start,
1672 upper_size: size_data.end,
1673 line_offset: symbol_instance.text_offset,
1674 writing_modes: writing_mode,
1675 line: symbol_instance.line().clone(),
1676 tile_distances: Self::calculate_tile_distances(
1677 symbol_instance.line(),
1678 &symbol_instance.anchor,
1679 ),
1680
1681 glyph_offsets: vec![],
1682 hidden: false,
1683 vertex_start_index: 0,
1684 cross_tile_id: 0,
1685 placed_orientation: None,
1686 angle: if self.allow_vertical_placement && writing_mode == WritingModeType::Vertical {
1687 PI / 2.
1688 } else {
1689 0.0
1690 },
1691 placed_icon_index,
1692 };
1693
1694 let mut first_symbol = true;
1695 for symbol_quad in glyph_quads {
1696 if has_format_section_overrides {
1697 if let Some(last_added_section) = last_added_section {
1698 if last_added_section != symbol_quad.section_index {
1699 self.update_paint_properties_for_section(
1700 bucket,
1701 feature,
1702 last_added_section,
1703 canonical,
1704 );
1705 }
1706 }
1707
1708 last_added_section = Some(symbol_quad.section_index);
1709 }
1710 let index = self.add_symbol(
1711 &mut bucket.text,
1712 size_data.clone(),
1713 symbol_quad,
1714 &symbol_instance.anchor,
1715 feature.sort_key,
1716 );
1717
1718 newly_placed_symbol
1719 .glyph_offsets
1720 .push(symbol_quad.glyph_offset.x);
1721
1722 if first_symbol {
1723 newly_placed_symbol.vertex_start_index = index;
1724 first_symbol = false;
1725 }
1726 }
1727
1728 bucket.text.placed_symbols.push(newly_placed_symbol);
1729 output_placed_index = Some(bucket.text.placed_symbols.len() - 1);
1730
1731 if let Some(last_added_section) = last_added_section {
1732 (last_added_section, output_placed_index)
1733 } else {
1734 (0, output_placed_index)
1735 }
1736 }
1737
1738 fn update_paint_properties_for_section(
1740 &self,
1741 bucket: &SymbolBucket,
1742 feature: &SymbolGeometryTileFeature,
1743 section_index: usize,
1744 canonical: &CanonicalTileID,
1745 ) -> usize {
1746 let formatted_section = section_options_to_value(
1747 feature
1748 .formatted_text
1749 .as_ref()
1750 .unwrap()
1751 .section_at(section_index),
1752 );
1753 0
1766 }
1767}
1768
1769#[cfg(test)]
1770mod tests {
1771 use std::collections::HashMap;
1772
1773 use crate::{
1774 euclid::{Point2D, Rect, Size2D},
1775 legacy::{
1776 bidi::Char16,
1777 font_stack::FontStackHasher,
1778 geometry_tile_data::{GeometryCoordinates, SymbolGeometryTileLayer},
1779 glyph::{Glyph, GlyphDependencies, GlyphMap, GlyphMetrics, Glyphs},
1780 glyph_atlas::{GlyphPosition, GlyphPositionMap, GlyphPositions},
1781 image::ImageMap,
1782 image_atlas::ImagePositions,
1783 layout::{
1784 layout::{BucketParameters, LayerTypeInfo, LayoutParameters},
1785 symbol_feature::{SymbolGeometryTileFeature, VectorGeometryTileFeature},
1786 symbol_layout::{FeatureIndex, LayerProperties, SymbolLayer, SymbolLayout},
1787 },
1788 style_types::SymbolLayoutProperties_Unevaluated,
1789 tagged_string::SectionOptions,
1790 CanonicalTileID, MapMode, OverscaledTileID,
1791 },
1792 };
1793
1794 #[test]
1795 fn test() {
1797 let font_stack = vec![
1798 "Open Sans Regular".to_string(),
1799 "Arial Unicode MS Regular".to_string(),
1800 ];
1801
1802 let section_options = SectionOptions::new(1.0, font_stack.clone(), None);
1803
1804 let mut glyph_dependencies = GlyphDependencies::new();
1805
1806 let tile_id = OverscaledTileID {
1807 canonical: CanonicalTileID { x: 0, y: 0, z: 0 },
1808 overscaled_z: 0,
1809 };
1810 let mut parameters = BucketParameters {
1811 tile_id: tile_id,
1812 mode: MapMode::Continuous,
1813 pixel_ratio: 1.0,
1814 layer_type: LayerTypeInfo,
1815 };
1816 let layer_data = SymbolGeometryTileLayer {
1817 name: "layer".to_string(),
1818 features: vec![SymbolGeometryTileFeature::new(Box::new(
1819 VectorGeometryTileFeature {
1820 geometry: vec![GeometryCoordinates(vec![Point2D::new(1024, 1024)])],
1821 },
1822 ))],
1823 };
1824 let layer_properties = vec![LayerProperties {
1825 id: "layer".to_string(),
1826 layer: SymbolLayer {
1827 layout: SymbolLayoutProperties_Unevaluated,
1828 },
1829 }];
1830
1831 let image_positions = ImagePositions::new();
1832
1833 let mut glyph_position = GlyphPosition {
1834 rect: Rect::new(Point2D::new(0, 0), Size2D::new(10, 10)),
1835 metrics: GlyphMetrics {
1836 width: 18,
1837 height: 18,
1838 left: 2,
1839 top: -8,
1840 advance: 21,
1841 },
1842 };
1843 let glyph_positions: GlyphPositions = GlyphPositions::from([(
1844 FontStackHasher::new(&font_stack),
1845 GlyphPositionMap::from([('中' as Char16, glyph_position)]),
1846 )]);
1847
1848 let mut glyph = Glyph::default();
1849 glyph.id = '中' as Char16;
1850 glyph.metrics = glyph_position.metrics;
1851
1852 let glyphs: GlyphMap = GlyphMap::from([(
1853 FontStackHasher::new(&font_stack),
1854 Glyphs::from([('中' as Char16, Some(glyph))]),
1855 )]);
1856
1857 let mut layout = SymbolLayout::new(
1858 ¶meters,
1859 &layer_properties,
1860 Box::new(layer_data),
1861 &mut LayoutParameters {
1862 bucket_parameters: &mut parameters.clone(),
1863 glyph_dependencies: &mut glyph_dependencies,
1864 image_dependencies: &mut Default::default(),
1865 available_images: &mut Default::default(),
1866 },
1867 )
1868 .unwrap();
1869
1870 assert_eq!(glyph_dependencies.len(), 1);
1871
1872 let empty_image_map = ImageMap::new();
1873 layout.prepare_symbols(
1874 &glyphs,
1875 &glyph_positions,
1876 &empty_image_map,
1877 &image_positions,
1878 );
1879
1880 let mut output = HashMap::new();
1881 layout.create_bucket(
1882 image_positions,
1883 Box::new(FeatureIndex),
1884 &mut output,
1885 false,
1886 false,
1887 &tile_id.canonical,
1888 );
1889
1890 println!(
1891 "{:#?}",
1892 output.get("layer").unwrap().bucket.text.shared_vertices
1893 )
1894 }
1895}