1use api::{ExternalScrollId, PropertyBinding, ReferenceFrameKind, TransformStyle, PropertyBindingId};
6use api::{APZScrollGeneration, HasScrollLinkedEffect, PipelineId, SampledScrollOffset};
7use api::units::*;
8use euclid::Transform3D;
9use crate::transform::TransformPalette;
10use crate::internal_types::{FastHashMap, FrameMemory};
11use crate::print_tree::{PrintableTree, PrintTree, PrintTreePrinter};
12use crate::scene::SceneProperties;
13use crate::spatial_node::{ReferenceFrameInfo, SpatialNode, SpatialNodeDescriptor, SpatialNodeType, StickyFrameInfo};
14use crate::spatial_node::{ScrollFrameKind, SceneSpatialNode, SpatialNodeInfo};
15use std::{ops, u32};
16use crate::util::{FastTransform, LayoutToWorldFastTransform, MatrixHelpers, ScaleOffset, scale_factors};
17use smallvec::SmallVec;
18use crate::util::TransformedRectKind;
19use peek_poke::PeekPoke;
20
21
22#[derive(Copy, Clone, PartialEq, PartialOrd)]
27#[cfg_attr(feature = "capture", derive(Serialize))]
28#[cfg_attr(feature = "replay", derive(Deserialize))]
29pub struct CoordinateSystemId(pub u32);
30
31impl std::fmt::Debug for CoordinateSystemId {
32 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
33 write!(f, "#{}", self.0)
34 }
35}
36
37#[derive(Debug)]
40#[cfg_attr(feature = "capture", derive(Serialize))]
41#[cfg_attr(feature = "replay", derive(Deserialize))]
42pub struct CoordinateSystem {
43 pub transform: LayoutTransform,
44 pub world_transform: LayoutToWorldTransform,
45 pub should_flatten: bool,
46 pub parent: Option<CoordinateSystemId>,
47}
48
49impl CoordinateSystem {
50 fn root() -> Self {
51 CoordinateSystem {
52 transform: LayoutTransform::identity(),
53 world_transform: LayoutToWorldTransform::identity(),
54 should_flatten: false,
55 parent: None,
56 }
57 }
58}
59
60#[derive(Copy, Clone, Eq, Hash, MallocSizeOf, PartialEq, PeekPoke, Default)]
61#[cfg_attr(feature = "capture", derive(Serialize))]
62#[cfg_attr(feature = "replay", derive(Deserialize))]
63pub struct SpatialNodeIndex(pub u32);
64
65impl SpatialNodeIndex {
66 pub const INVALID: SpatialNodeIndex = SpatialNodeIndex(u32::MAX);
67
68 pub const UNKNOWN: SpatialNodeIndex = SpatialNodeIndex(u32::MAX - 1);
74}
75
76impl std::fmt::Debug for SpatialNodeIndex {
77 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
78 if *self == Self::INVALID {
79 write!(f, "<invalid>")
80 } else if *self == Self::UNKNOWN {
81 write!(f, "<unknown>")
82 } else {
83 write!(f, "#{}", self.0)
84 }
85 }
86}
87
88const MIN_SCROLLABLE_AMOUNT: f32 = 0.01;
92
93const MIN_SCROLL_ROOT_SIZE: f32 = 128.0;
95
96impl SpatialNodeIndex {
97 pub fn new(index: usize) -> Self {
98 debug_assert!(index < ::std::u32::MAX as usize);
99 SpatialNodeIndex(index as u32)
100 }
101}
102
103impl CoordinateSystemId {
104 pub fn root() -> Self {
105 CoordinateSystemId(0)
106 }
107}
108
109#[derive(Debug, Copy, Clone, PartialEq)]
110pub enum VisibleFace {
111 Front,
112 Back,
113}
114
115impl Default for VisibleFace {
116 fn default() -> Self {
117 VisibleFace::Front
118 }
119}
120
121impl ops::Not for VisibleFace {
122 type Output = Self;
123 fn not(self) -> Self {
124 match self {
125 VisibleFace::Front => VisibleFace::Back,
126 VisibleFace::Back => VisibleFace::Front,
127 }
128 }
129}
130
131pub trait SpatialNodeContainer {
134 fn get_node_info(&self, index: SpatialNodeIndex) -> SpatialNodeInfo;
136
137 fn get_snapping_info(
138 &self,
139 parent_index: Option<SpatialNodeIndex>
140 ) -> Option<ScaleOffset> {
141 match parent_index {
142 Some(parent_index) => {
143 let node_info = self.get_node_info(parent_index);
144 node_info.snapping_transform
145 }
146 None => {
147 Some(ScaleOffset::identity())
148 }
149 }
150 }
151}
152
153#[cfg_attr(feature = "capture", derive(Serialize))]
161#[cfg_attr(feature = "replay", derive(Deserialize))]
162pub struct SceneSpatialTree {
163 spatial_nodes: Vec<SceneSpatialNode>,
166
167 root_reference_frame_index: SpatialNodeIndex,
168
169 updates: SpatialTreeUpdates,
170}
171
172impl SpatialNodeContainer for SceneSpatialTree {
173 fn get_node_info(&self, index: SpatialNodeIndex) -> SpatialNodeInfo {
174 let node = &self.spatial_nodes[index.0 as usize];
175
176 SpatialNodeInfo {
177 parent: node.parent,
178 node_type: &node.descriptor.node_type,
179 snapping_transform: node.snapping_transform,
180 }
181 }
182}
183
184impl SceneSpatialTree {
185 pub fn new() -> Self {
186 let mut tree = SceneSpatialTree {
187 spatial_nodes: Vec::new(),
188 root_reference_frame_index: SpatialNodeIndex(0),
189 updates: SpatialTreeUpdates::new(),
190 };
191
192 tree.add_root_reference_frame();
193
194 tree
195 }
196
197 pub fn reset(&mut self) {
200 self.spatial_nodes.clear();
201 self.updates = SpatialTreeUpdates::new();
202 self.add_root_reference_frame();
203 }
204
205 fn add_root_reference_frame(&mut self) {
206 let node = SceneSpatialNode::new_reference_frame(
207 None,
208 TransformStyle::Flat,
209 PropertyBinding::Value(LayoutTransform::identity()),
210 ReferenceFrameKind::Transform {
211 should_snap: true,
212 is_2d_scale_translation: true,
213 paired_with_perspective: false,
214 },
215 LayoutVector2D::zero(),
216 PipelineId::dummy(),
217 true,
218 true,
219 );
220
221 let index = self.add_spatial_node(node);
222 debug_assert_eq!(index, SpatialNodeIndex(0));
223 }
224
225 pub fn is_root_coord_system(&self, index: SpatialNodeIndex) -> bool {
226 self.spatial_nodes[index.0 as usize].is_root_coord_system
227 }
228
229 pub fn end_frame_and_get_pending_updates(&mut self) -> SpatialTreeUpdates {
231 self.updates.root_reference_frame_index = self.root_reference_frame_index;
232 std::mem::replace(&mut self.updates, SpatialTreeUpdates::new())
233 }
234
235 pub fn is_ancestor(
237 &self,
238 maybe_parent: SpatialNodeIndex,
239 maybe_child: SpatialNodeIndex,
240 ) -> bool {
241 if maybe_parent == maybe_child {
243 return false;
244 }
245
246 let mut current_node = maybe_child;
247
248 while current_node != self.root_reference_frame_index {
249 let node = self.get_node_info(current_node);
250 current_node = node.parent.expect("bug: no parent");
251
252 if current_node == maybe_parent {
253 return true;
254 }
255 }
256
257 false
258 }
259
260 pub fn find_scroll_root(
264 &self,
265 spatial_node_index: SpatialNodeIndex,
266 allow_sticky_frames: bool,
267 ) -> SpatialNodeIndex {
268 let mut real_scroll_root = self.root_reference_frame_index;
269 let mut outermost_scroll_root = self.root_reference_frame_index;
270 let mut current_scroll_root_is_sticky = false;
271 let mut node_index = spatial_node_index;
272
273 while node_index != self.root_reference_frame_index {
274 let node = self.get_node_info(node_index);
275 match node.node_type {
276 SpatialNodeType::ReferenceFrame(ref info) => {
277 match info.kind {
278 ReferenceFrameKind::Transform { is_2d_scale_translation: true, .. } => {
279 }
281 ReferenceFrameKind::Transform { is_2d_scale_translation: false, .. } |
282 ReferenceFrameKind::Perspective { .. } => {
283 real_scroll_root = self.root_reference_frame_index;
286 outermost_scroll_root = self.root_reference_frame_index;
287 current_scroll_root_is_sticky = false;
288 }
289 }
290 }
291 SpatialNodeType::StickyFrame(..) => {
292 if allow_sticky_frames {
295 outermost_scroll_root = node_index;
296 real_scroll_root = node_index;
297 current_scroll_root_is_sticky = true;
300 }
301 }
302 SpatialNodeType::ScrollFrame(ref info) => {
303 match info.frame_kind {
304 ScrollFrameKind::PipelineRoot { is_root_pipeline } => {
305 if is_root_pipeline {
307 break;
308 }
309 }
310 ScrollFrameKind::Explicit => {
311 outermost_scroll_root = node_index;
314
315 if !current_scroll_root_is_sticky {
319 if info.scrollable_size.width > MIN_SCROLLABLE_AMOUNT ||
324 info.scrollable_size.height > MIN_SCROLLABLE_AMOUNT {
325 if info.viewport_rect.width() > MIN_SCROLL_ROOT_SIZE &&
333 info.viewport_rect.height() > MIN_SCROLL_ROOT_SIZE {
334 real_scroll_root = node_index;
337 }
338 }
339 }
340 }
341 }
342 }
343 }
344 node_index = node.parent.expect("unable to find parent node");
345 }
346
347 if real_scroll_root == self.root_reference_frame_index {
353 outermost_scroll_root
354 } else {
355 real_scroll_root
356 }
357 }
358
359 pub fn root_reference_frame_index(&self) -> SpatialNodeIndex {
361 self.root_reference_frame_index
362 }
363
364 fn add_spatial_node(
365 &mut self,
366 mut node: SceneSpatialNode,
367 ) -> SpatialNodeIndex {
368 let parent_info = self.get_snapping_info(node.parent);
369
370 node.snapping_transform = calculate_snapping_transform(
371 parent_info,
372 &node.descriptor.node_type,
373 );
374
375 let descriptor = node.descriptor.clone();
376 let parent = node.parent;
377
378 let index = self.spatial_nodes.len();
379 self.spatial_nodes.push(node);
380
381 self.updates.updates.push(SpatialTreeUpdate {
382 index,
383 descriptor,
384 parent,
385 });
386
387 SpatialNodeIndex(index as u32)
388 }
389
390 pub fn add_reference_frame(
391 &mut self,
392 parent_index: SpatialNodeIndex,
393 transform_style: TransformStyle,
394 source_transform: PropertyBinding<LayoutTransform>,
395 kind: ReferenceFrameKind,
396 origin_in_parent_reference_frame: LayoutVector2D,
397 pipeline_id: PipelineId,
398 is_pipeline_root: bool,
399 ) -> SpatialNodeIndex {
400 let new_static_coord_system = match kind {
402 ReferenceFrameKind::Transform { is_2d_scale_translation: true, .. } => {
403 false
405 }
406 ReferenceFrameKind::Transform { is_2d_scale_translation: false, .. } | ReferenceFrameKind::Perspective { .. } => {
407 match source_transform {
411 PropertyBinding::Value(m) => {
412 !m.is_2d_scale_translation()
413 }
414 PropertyBinding::Binding(..) => {
415 true
417 }
418 }
419 }
420 };
421
422 let is_root_coord_system = !new_static_coord_system &&
423 self.spatial_nodes[parent_index.0 as usize].is_root_coord_system;
424
425 let node = SceneSpatialNode::new_reference_frame(
426 Some(parent_index),
427 transform_style,
428 source_transform,
429 kind,
430 origin_in_parent_reference_frame,
431 pipeline_id,
432 is_root_coord_system,
433 is_pipeline_root,
434 );
435 self.add_spatial_node(node)
436 }
437
438 pub fn add_scroll_frame(
439 &mut self,
440 parent_index: SpatialNodeIndex,
441 external_id: ExternalScrollId,
442 pipeline_id: PipelineId,
443 frame_rect: &LayoutRect,
444 content_size: &LayoutSize,
445 frame_kind: ScrollFrameKind,
446 external_scroll_offset: LayoutVector2D,
447 scroll_offset_generation: APZScrollGeneration,
448 has_scroll_linked_effect: HasScrollLinkedEffect,
449 ) -> SpatialNodeIndex {
450 let is_root_coord_system = self.spatial_nodes[parent_index.0 as usize].is_root_coord_system;
452
453 let node = SceneSpatialNode::new_scroll_frame(
454 pipeline_id,
455 parent_index,
456 external_id,
457 frame_rect,
458 content_size,
459 frame_kind,
460 external_scroll_offset,
461 scroll_offset_generation,
462 has_scroll_linked_effect,
463 is_root_coord_system,
464 );
465 self.add_spatial_node(node)
466 }
467
468 pub fn add_sticky_frame(
469 &mut self,
470 parent_index: SpatialNodeIndex,
471 sticky_frame_info: StickyFrameInfo,
472 pipeline_id: PipelineId,
473 ) -> SpatialNodeIndex {
474 let is_root_coord_system = self.spatial_nodes[parent_index.0 as usize].is_root_coord_system;
476
477 let node = SceneSpatialNode::new_sticky_frame(
478 parent_index,
479 sticky_frame_info,
480 pipeline_id,
481 is_root_coord_system,
482 );
483 self.add_spatial_node(node)
484 }
485}
486
487#[cfg_attr(feature = "capture", derive(Serialize))]
488#[cfg_attr(feature = "replay", derive(Deserialize))]
489pub struct SpatialTreeUpdate {
490 pub index: usize,
491 pub parent: Option<SpatialNodeIndex>,
492 pub descriptor: SpatialNodeDescriptor,
493}
494
495#[cfg_attr(feature = "capture", derive(Serialize))]
498#[cfg_attr(feature = "replay", derive(Deserialize))]
499pub struct SpatialTreeUpdates {
500 root_reference_frame_index: SpatialNodeIndex,
501 updates: Vec<SpatialTreeUpdate>,
502}
503
504impl SpatialTreeUpdates {
505 fn new() -> Self {
506 SpatialTreeUpdates {
507 root_reference_frame_index: SpatialNodeIndex::INVALID,
508 updates: Vec::new(),
509 }
510 }
511}
512
513#[cfg_attr(feature = "capture", derive(Serialize))]
516#[cfg_attr(feature = "replay", derive(Deserialize))]
517pub struct SpatialTree {
518 spatial_nodes: Vec<SpatialNode>,
521
522 coord_systems: Vec<CoordinateSystem>,
526
527 root_reference_frame_index: SpatialNodeIndex,
528
529 update_state_stack: Vec<TransformUpdateState>,
531}
532
533#[derive(Clone)]
534#[cfg_attr(feature = "capture", derive(Serialize))]
535#[cfg_attr(feature = "replay", derive(Deserialize))]
536pub struct TransformUpdateState {
537 pub parent_reference_frame_transform: LayoutToWorldFastTransform,
538 pub parent_accumulated_scroll_offset: LayoutVector2D,
539 pub nearest_scrolling_ancestor_offset: LayoutVector2D,
540 pub nearest_scrolling_ancestor_viewport: LayoutRect,
541
542 pub current_coordinate_system_id: CoordinateSystemId,
547
548 pub coordinate_system_relative_scale_offset: ScaleOffset,
550
551 pub invertible: bool,
555
556 pub preserves_3d: bool,
558
559 pub is_ancestor_or_self_zooming: bool,
561
562 pub external_id: Option<ExternalScrollId>,
564
565 pub scroll_offset: LayoutVector2D,
567}
568
569#[derive(Debug, Clone)]
572pub enum CoordinateSpaceMapping<Src, Dst> {
573 Local,
574 ScaleOffset(ScaleOffset),
575 Transform(Transform3D<f32, Src, Dst>),
576}
577
578impl<Src, Dst> CoordinateSpaceMapping<Src, Dst> {
579 pub fn into_transform(self) -> Transform3D<f32, Src, Dst> {
580 match self {
581 CoordinateSpaceMapping::Local => Transform3D::identity(),
582 CoordinateSpaceMapping::ScaleOffset(scale_offset) => scale_offset.to_transform(),
583 CoordinateSpaceMapping::Transform(transform) => transform,
584 }
585 }
586
587 pub fn into_fast_transform(self) -> FastTransform<Src, Dst> {
588 match self {
589 CoordinateSpaceMapping::Local => FastTransform::identity(),
590 CoordinateSpaceMapping::ScaleOffset(scale_offset) => FastTransform::with_scale_offset(scale_offset),
591 CoordinateSpaceMapping::Transform(transform) => FastTransform::with_transform(transform),
592 }
593 }
594
595 pub fn is_perspective(&self) -> bool {
596 match *self {
597 CoordinateSpaceMapping::Local |
598 CoordinateSpaceMapping::ScaleOffset(_) => false,
599 CoordinateSpaceMapping::Transform(ref transform) => transform.has_perspective_component(),
600 }
601 }
602
603 pub fn is_2d_axis_aligned(&self) -> bool {
604 match *self {
605 CoordinateSpaceMapping::Local |
606 CoordinateSpaceMapping::ScaleOffset(_) => true,
607 CoordinateSpaceMapping::Transform(ref transform) => transform.preserves_2d_axis_alignment(),
608 }
609 }
610
611 pub fn is_2d_scale_translation(&self) -> bool {
612 match *self {
613 CoordinateSpaceMapping::Local |
614 CoordinateSpaceMapping::ScaleOffset(_) => true,
615 CoordinateSpaceMapping::Transform(ref transform) => transform.is_2d_scale_translation(),
616 }
617 }
618
619 pub fn scale_factors(&self) -> (f32, f32) {
620 match *self {
621 CoordinateSpaceMapping::Local => (1.0, 1.0),
622 CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => (scale_offset.scale.x.abs(), scale_offset.scale.y.abs()),
623 CoordinateSpaceMapping::Transform(ref transform) => scale_factors(transform),
624 }
625 }
626
627 pub fn inverse(&self) -> Option<CoordinateSpaceMapping<Dst, Src>> {
628 match *self {
629 CoordinateSpaceMapping::Local => Some(CoordinateSpaceMapping::Local),
630 CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
631 Some(CoordinateSpaceMapping::ScaleOffset(scale_offset.inverse()))
632 }
633 CoordinateSpaceMapping::Transform(ref transform) => {
634 transform.inverse().map(CoordinateSpaceMapping::Transform)
635 }
636 }
637 }
638
639 pub fn as_2d_scale_offset(&self) -> Option<ScaleOffset> {
640 Some(match *self {
641 CoordinateSpaceMapping::Local => ScaleOffset::identity(),
642 CoordinateSpaceMapping::ScaleOffset(transfrom) => transfrom,
643 CoordinateSpaceMapping::Transform(ref transform) => {
644 if !transform.is_2d_scale_translation() {
645 return None
646 }
647 ScaleOffset::new(transform.m11, transform.m22, transform.m41, transform.m42)
648 }
649 })
650 }
651}
652
653enum TransformScroll {
654 Scrolled,
655 Unscrolled,
656}
657
658impl SpatialNodeContainer for SpatialTree {
659 fn get_node_info(&self, index: SpatialNodeIndex) -> SpatialNodeInfo {
660 let node = self.get_spatial_node(index);
661
662 SpatialNodeInfo {
663 parent: node.parent,
664 node_type: &node.node_type,
665 snapping_transform: node.snapping_transform,
666 }
667 }
668}
669
670impl SpatialTree {
671 pub fn new() -> Self {
672 SpatialTree {
673 spatial_nodes: Vec::new(),
674 coord_systems: Vec::new(),
675 root_reference_frame_index: SpatialNodeIndex::INVALID,
676 update_state_stack: Vec::new(),
677 }
678 }
679
680 fn visit_node_impl_mut<F>(
681 &mut self,
682 index: SpatialNodeIndex,
683 f: &mut F,
684 ) where F: FnMut(SpatialNodeIndex, &mut SpatialNode) {
685 let mut child_indices: SmallVec<[SpatialNodeIndex; 8]> = SmallVec::new();
686
687 let node = self.get_spatial_node_mut(index);
688 f(index, node);
689 child_indices.extend_from_slice(&node.children);
690
691 for child_index in child_indices {
692 self.visit_node_impl_mut(child_index, f);
693 }
694 }
695
696 fn visit_node_impl<F>(
697 &self,
698 index: SpatialNodeIndex,
699 f: &mut F,
700 ) where F: FnMut(SpatialNodeIndex, &SpatialNode) {
701 let node = self.get_spatial_node(index);
702
703 f(index, node);
704
705 for child_index in &node.children {
706 self.visit_node_impl(*child_index, f);
707 }
708 }
709
710 pub fn visit_nodes<F>(&self, mut f: F) where F: FnMut(SpatialNodeIndex, &SpatialNode) {
712 if self.root_reference_frame_index == SpatialNodeIndex::INVALID {
713 return;
714 }
715
716 self.visit_node_impl(self.root_reference_frame_index, &mut f);
717 }
718
719 pub fn visit_nodes_mut<F>(&mut self, mut f: F) where F: FnMut(SpatialNodeIndex, &mut SpatialNode) {
721 if self.root_reference_frame_index == SpatialNodeIndex::INVALID {
722 return;
723 }
724
725 self.visit_node_impl_mut(self.root_reference_frame_index, &mut f);
726 }
727
728 pub fn apply_updates(
730 &mut self,
731 updates: SpatialTreeUpdates,
732 ) {
733 self.root_reference_frame_index = updates.root_reference_frame_index;
734 self.spatial_nodes.clear();
735
736 for SpatialTreeUpdate { index, parent, descriptor } in updates.updates {
737 debug_assert_eq!(index, self.spatial_nodes.len());
738
739 if let Some(parent) = parent {
740 self.get_spatial_node_mut(parent).add_child(SpatialNodeIndex(index as u32));
741 }
742
743 self.spatial_nodes.push(SpatialNode {
744 viewport_transform: ScaleOffset::identity(),
745 content_transform: ScaleOffset::identity(),
746 snapping_transform: None,
747 coordinate_system_id: CoordinateSystemId(0),
748 transform_kind: TransformedRectKind::AxisAligned,
749 parent,
750 children: Vec::new(),
751 pipeline_id: descriptor.pipeline_id,
752 node_type: descriptor.node_type,
753 invertible: true,
754 is_async_zooming: false,
755 is_ancestor_or_self_zooming: false,
756 });
757 }
758
759 self.visit_nodes_mut(|_, node| {
760 match node.node_type {
761 SpatialNodeType::ScrollFrame(ref mut info) => {
762 info.offsets = vec![SampledScrollOffset{
763 offset: -info.external_scroll_offset,
764 generation: info.offset_generation,
765 }];
766 }
767 SpatialNodeType::StickyFrame(ref mut info) => {
768 info.current_offset = LayoutVector2D::zero();
769 }
770 SpatialNodeType::ReferenceFrame(..) => {}
771 }
772 });
773 }
774
775 pub fn get_last_sampled_scroll_offsets(
776 &self,
777 ) -> FastHashMap<ExternalScrollId, Vec<SampledScrollOffset>> {
778 let mut result = FastHashMap::default();
779 self.visit_nodes(|_, node| {
780 if let SpatialNodeType::ScrollFrame(ref scrolling) = node.node_type {
781 result.insert(scrolling.external_id, scrolling.offsets.clone());
782 }
783 });
784 result
785 }
786
787 pub fn apply_last_sampled_scroll_offsets(
788 &mut self,
789 last_sampled_offsets: FastHashMap<ExternalScrollId, Vec<SampledScrollOffset>>,
790 ) {
791 self.visit_nodes_mut(|_, node| {
792 if let SpatialNodeType::ScrollFrame(ref mut scrolling) = node.node_type {
793 if let Some(offsets) = last_sampled_offsets.get(&scrolling.external_id) {
794 scrolling.offsets = offsets.clone();
795 }
796 }
797 });
798 }
799
800 pub fn get_spatial_node(&self, index: SpatialNodeIndex) -> &SpatialNode {
801 &self.spatial_nodes[index.0 as usize]
802 }
803
804 pub fn get_spatial_node_mut(&mut self, index: SpatialNodeIndex) -> &mut SpatialNode {
805 &mut self.spatial_nodes[index.0 as usize]
806 }
807
808 pub fn spatial_node_count(&self) -> usize {
810 self.spatial_nodes.len()
811 }
812
813 pub fn find_spatial_node_by_anim_id(
814 &self,
815 id: PropertyBindingId,
816 ) -> Option<SpatialNodeIndex> {
817 let mut node_index = None;
818
819 self.visit_nodes(|index, node| {
820 if node.is_transform_bound_to_property(id) {
821 debug_assert!(node_index.is_none()); node_index = Some(index);
823 }
824 });
825
826 node_index
827 }
828
829 pub fn get_relative_transform(
832 &self,
833 child_index: SpatialNodeIndex,
834 parent_index: SpatialNodeIndex,
835 ) -> CoordinateSpaceMapping<LayoutPixel, LayoutPixel> {
836 self.get_relative_transform_with_face(child_index, parent_index, None)
837 }
838
839 pub fn get_relative_transform_with_face(
844 &self,
845 child_index: SpatialNodeIndex,
846 parent_index: SpatialNodeIndex,
847 mut visible_face: Option<&mut VisibleFace>,
848 ) -> CoordinateSpaceMapping<LayoutPixel, LayoutPixel> {
849 if child_index == parent_index {
850 return CoordinateSpaceMapping::Local;
851 }
852
853 let child = self.get_spatial_node(child_index);
854 let parent = self.get_spatial_node(parent_index);
855
856 assert!(
861 child.coordinate_system_id.0 >= parent.coordinate_system_id.0,
862 "bug: this is an unexpected case - please open a bug and talk to #gfx team!",
863 );
864
865 if child.coordinate_system_id == parent.coordinate_system_id {
866 let scale_offset = child.content_transform.then(&parent.content_transform.inverse());
867
868 if scale_offset.is_identity() {
871 return CoordinateSpaceMapping::Local;
872 }
873
874 return CoordinateSpaceMapping::ScaleOffset(scale_offset);
875 }
876
877 let mut coordinate_system_id = child.coordinate_system_id;
878 let mut transform = child.content_transform.to_transform();
879
880 while coordinate_system_id != parent.coordinate_system_id {
885 let coord_system = &self.coord_systems[coordinate_system_id.0 as usize];
886
887 if coord_system.should_flatten {
888 if let Some(ref mut face) = visible_face {
889 if transform.is_backface_visible() {
890 **face = VisibleFace::Back;
891 }
892 }
893 transform.flatten_z_output();
894 }
895
896 coordinate_system_id = coord_system.parent.expect("invalid parent!");
897 transform = transform.then(&coord_system.transform);
898 }
899
900 transform = transform.then(
901 &parent.content_transform
902 .inverse()
903 .to_transform(),
904 );
905 if let Some(face) = visible_face {
906 if transform.is_backface_visible() {
907 *face = VisibleFace::Back;
908 }
909 }
910
911 CoordinateSpaceMapping::Transform(transform)
912 }
913
914 pub fn is_matching_coord_system(
917 &self,
918 index0: SpatialNodeIndex,
919 index1: SpatialNodeIndex,
920 ) -> bool {
921 let node0 = self.get_spatial_node(index0);
922 let node1 = self.get_spatial_node(index1);
923
924 node0.coordinate_system_id == node1.coordinate_system_id
925 }
926
927 fn get_world_transform_impl(
928 &self,
929 index: SpatialNodeIndex,
930 scroll: TransformScroll,
931 ) -> CoordinateSpaceMapping<LayoutPixel, WorldPixel> {
932 let child = self.get_spatial_node(index);
933
934 if child.coordinate_system_id.0 == 0 {
935 if index == self.root_reference_frame_index {
936 CoordinateSpaceMapping::Local
937 } else {
938 match scroll {
939 TransformScroll::Scrolled => CoordinateSpaceMapping::ScaleOffset(child.content_transform),
940 TransformScroll::Unscrolled => CoordinateSpaceMapping::ScaleOffset(child.viewport_transform),
941 }
942 }
943 } else {
944 let system = &self.coord_systems[child.coordinate_system_id.0 as usize];
945 let scale_offset = match scroll {
946 TransformScroll::Scrolled => &child.content_transform,
947 TransformScroll::Unscrolled => &child.viewport_transform,
948 };
949 let transform = scale_offset
950 .to_transform()
951 .then(&system.world_transform);
952
953 CoordinateSpaceMapping::Transform(transform)
954 }
955 }
956
957 pub fn get_world_transform(
959 &self,
960 index: SpatialNodeIndex,
961 ) -> CoordinateSpaceMapping<LayoutPixel, WorldPixel> {
962 self.get_world_transform_impl(index, TransformScroll::Scrolled)
963 }
964
965 pub fn get_world_viewport_transform(
968 &self,
969 index: SpatialNodeIndex,
970 ) -> CoordinateSpaceMapping<LayoutPixel, WorldPixel> {
971 self.get_world_transform_impl(index, TransformScroll::Unscrolled)
972 }
973
974 pub fn root_reference_frame_index(&self) -> SpatialNodeIndex {
976 self.root_reference_frame_index
977 }
978
979 pub fn set_scroll_offsets(
980 &mut self,
981 id: ExternalScrollId,
982 offsets: Vec<SampledScrollOffset>,
983 ) -> bool {
984 let mut did_change = false;
985
986 self.visit_nodes_mut(|_, node| {
987 if node.matches_external_id(id) {
988 did_change |= node.set_scroll_offsets(offsets.clone());
989 }
990 });
991
992 did_change
993 }
994
995 pub fn update_tree(
996 &mut self,
997 scene_properties: &SceneProperties,
998 ) {
999 if self.root_reference_frame_index == SpatialNodeIndex::INVALID {
1000 return;
1001 }
1002
1003 profile_scope!("update_tree");
1004 self.coord_systems.clear();
1005 self.coord_systems.push(CoordinateSystem::root());
1006
1007 let root_node_index = self.root_reference_frame_index();
1008 assert!(self.update_state_stack.is_empty());
1009
1010 let state = TransformUpdateState {
1011 parent_reference_frame_transform: LayoutVector2D::zero().into(),
1012 parent_accumulated_scroll_offset: LayoutVector2D::zero(),
1013 nearest_scrolling_ancestor_offset: LayoutVector2D::zero(),
1014 nearest_scrolling_ancestor_viewport: LayoutRect::zero(),
1015 current_coordinate_system_id: CoordinateSystemId::root(),
1016 coordinate_system_relative_scale_offset: ScaleOffset::identity(),
1017 invertible: true,
1018 preserves_3d: false,
1019 is_ancestor_or_self_zooming: false,
1020 external_id: None,
1021 scroll_offset: LayoutVector2D::zero(),
1022 };
1023 self.update_state_stack.push(state);
1024
1025 self.update_node(
1026 root_node_index,
1027 scene_properties,
1028 );
1029
1030 self.update_state_stack.pop().unwrap();
1031 }
1032
1033 fn update_node(
1034 &mut self,
1035 node_index: SpatialNodeIndex,
1036 scene_properties: &SceneProperties,
1037 ) {
1038 let parent_index = self.get_spatial_node(node_index).parent;
1039 let parent_info = self.get_snapping_info(parent_index);
1040
1041 let node = &mut self.spatial_nodes[node_index.0 as usize];
1042
1043 node.snapping_transform = calculate_snapping_transform(
1044 parent_info,
1045 &node.node_type,
1046 );
1047
1048 node.update(
1049 &self.update_state_stack,
1050 &mut self.coord_systems,
1051 scene_properties,
1052 );
1053
1054 if !node.children.is_empty() {
1055 let mut child_state = self.update_state_stack.last().unwrap().clone();
1056 node.prepare_state_for_children(&mut child_state);
1057 self.update_state_stack.push(child_state);
1058
1059 let mut child_indices: SmallVec<[SpatialNodeIndex; 8]> = SmallVec::new();
1060 child_indices.extend_from_slice(&node.children);
1061
1062 for child_index in child_indices {
1063 self.update_node(
1064 child_index,
1065 scene_properties,
1066 );
1067 }
1068
1069 self.update_state_stack.pop().unwrap();
1070 }
1071 }
1072
1073 pub fn build_transform_palette(&self, memory: &FrameMemory) -> TransformPalette {
1074 profile_scope!("build_transform_palette");
1075 TransformPalette::new(self.spatial_nodes.len(), memory)
1076 }
1077
1078 fn print_node<T: PrintTreePrinter>(
1079 &self,
1080 index: SpatialNodeIndex,
1081 pt: &mut T,
1082 ) {
1083 let node = self.get_spatial_node(index);
1084 match node.node_type {
1085 SpatialNodeType::StickyFrame(ref sticky_frame_info) => {
1086 pt.new_level(format!("StickyFrame"));
1087 pt.add_item(format!("sticky info: {:?}", sticky_frame_info));
1088 }
1089 SpatialNodeType::ScrollFrame(ref scrolling_info) => {
1090 pt.new_level(format!("ScrollFrame"));
1091 pt.add_item(format!("viewport: {:?}", scrolling_info.viewport_rect));
1092 pt.add_item(format!("scrollable_size: {:?}", scrolling_info.scrollable_size));
1093 pt.add_item(format!("scroll offset: {:?}", scrolling_info.offset()));
1094 pt.add_item(format!("external_scroll_offset: {:?}", scrolling_info.external_scroll_offset));
1095 pt.add_item(format!("offset generation: {:?}", scrolling_info.offset_generation));
1096 if scrolling_info.has_scroll_linked_effect == HasScrollLinkedEffect::Yes {
1097 pt.add_item("has scroll-linked effect".to_string());
1098 }
1099 pt.add_item(format!("kind: {:?}", scrolling_info.frame_kind));
1100 }
1101 SpatialNodeType::ReferenceFrame(ref info) => {
1102 pt.new_level(format!("ReferenceFrame"));
1103 pt.add_item(format!("kind: {:?}", info.kind));
1104 pt.add_item(format!("transform_style: {:?}", info.transform_style));
1105 pt.add_item(format!("source_transform: {:?}", info.source_transform));
1106 pt.add_item(format!("origin_in_parent_reference_frame: {:?}", info.origin_in_parent_reference_frame));
1107 }
1108 }
1109
1110 pt.add_item(format!("index: {:?}", index));
1111 pt.add_item(format!("content_transform: {:?}", node.content_transform));
1112 pt.add_item(format!("viewport_transform: {:?}", node.viewport_transform));
1113 pt.add_item(format!("snapping_transform: {:?}", node.snapping_transform));
1114 pt.add_item(format!("coordinate_system_id: {:?}", node.coordinate_system_id));
1115
1116 for child_index in &node.children {
1117 self.print_node(*child_index, pt);
1118 }
1119
1120 pt.end_level();
1121 }
1122
1123 pub fn get_local_visible_face(&self, node_index: SpatialNodeIndex) -> VisibleFace {
1125 let node = self.get_spatial_node(node_index);
1126 let mut face = VisibleFace::Front;
1127 if let Some(mut parent_index) = node.parent {
1128 if let SpatialNodeType::ReferenceFrame(ReferenceFrameInfo { kind: ReferenceFrameKind::Transform {
1135 paired_with_perspective: true,
1136 ..
1137 }, .. }) = node.node_type {
1138 let parent = self.get_spatial_node(parent_index);
1139 match parent.node_type {
1140 SpatialNodeType::ReferenceFrame(ReferenceFrameInfo {
1141 kind: ReferenceFrameKind::Perspective { .. },
1142 ..
1143 }) => {
1144 parent_index = parent.parent.unwrap();
1145 }
1146 _ => {
1147 log::error!("Unexpected parent {:?} is not perspective", parent_index);
1148 }
1149 }
1150 }
1151
1152 self.get_relative_transform_with_face(node_index, parent_index, Some(&mut face));
1153 }
1154 face
1155 }
1156
1157 #[allow(dead_code)]
1158 pub fn print_to_string(&self) -> String {
1159 let mut result = String::new();
1160
1161 if self.root_reference_frame_index != SpatialNodeIndex::INVALID {
1162 let mut buf = Vec::<u8>::new();
1163 {
1164 let mut pt = PrintTree::new_with_sink("spatial tree", &mut buf);
1165 self.print_with(&mut pt);
1166 }
1167 result = std::str::from_utf8(&buf).unwrap_or("(Tree printer emitted non-utf8)").to_string();
1168 }
1169
1170 result
1171 }
1172
1173 #[allow(dead_code)]
1174 pub fn print(&self) {
1175 let result = self.print_to_string();
1176 debug!("{}", result);
1179 }
1180}
1181
1182impl PrintableTree for SpatialTree {
1183 fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) {
1184 if self.root_reference_frame_index != SpatialNodeIndex::INVALID {
1185 self.print_node(self.root_reference_frame_index(), pt);
1186 }
1187 }
1188}
1189
1190pub fn get_external_scroll_offset<S: SpatialNodeContainer>(
1192 spatial_tree: &S,
1193 node_index: SpatialNodeIndex,
1194) -> LayoutVector2D {
1195 let mut offset = LayoutVector2D::zero();
1196 let mut current_node = Some(node_index);
1197
1198 while let Some(node_index) = current_node {
1199 let node_info = spatial_tree.get_node_info(node_index);
1200
1201 match node_info.node_type {
1202 SpatialNodeType::ScrollFrame(ref scrolling) => {
1203 offset += scrolling.external_scroll_offset;
1204 }
1205 SpatialNodeType::StickyFrame(ref sticky) => {
1206 offset -= sticky.previously_applied_offset;
1210 }
1211 SpatialNodeType::ReferenceFrame(..) => {
1212 break;
1215 }
1216 }
1217
1218 current_node = node_info.parent;
1219 }
1220
1221 offset
1222}
1223
1224fn calculate_snapping_transform(
1225 parent_scale_offset: Option<ScaleOffset>,
1226 node_type: &SpatialNodeType,
1227) -> Option<ScaleOffset> {
1228 let parent_scale_offset = match parent_scale_offset {
1233 Some(transform) => transform,
1234 None => return None,
1235 };
1236
1237 let scale_offset = match node_type {
1238 SpatialNodeType::ReferenceFrame(ref info) => {
1239 let origin_offset = info.origin_in_parent_reference_frame;
1240
1241 match info.source_transform {
1242 PropertyBinding::Value(ref value) => {
1243 match ScaleOffset::from_transform(value) {
1246 Some(scale_offset) => {
1247 scale_offset.then(&ScaleOffset::from_offset(origin_offset.to_untyped()))
1248 }
1249 None => return None,
1250 }
1251 }
1252
1253 PropertyBinding::Binding(..) => {
1257 ScaleOffset::from_offset(origin_offset.to_untyped())
1258 }
1259 }
1260 }
1261 _ => ScaleOffset::identity(),
1262 };
1263
1264 Some(scale_offset.then(&parent_scale_offset))
1265}
1266
1267#[cfg(test)]
1268fn add_reference_frame(
1269 cst: &mut SceneSpatialTree,
1270 parent: SpatialNodeIndex,
1271 transform: LayoutTransform,
1272 origin_in_parent_reference_frame: LayoutVector2D,
1273) -> SpatialNodeIndex {
1274 cst.add_reference_frame(
1275 parent,
1276 TransformStyle::Preserve3D,
1277 PropertyBinding::Value(transform),
1278 ReferenceFrameKind::Transform {
1279 is_2d_scale_translation: false,
1280 should_snap: false,
1281 paired_with_perspective: false,
1282 },
1283 origin_in_parent_reference_frame,
1284 PipelineId::dummy(),
1285 false,
1286 )
1287}
1288
1289#[cfg(test)]
1290fn test_pt(
1291 px: f32,
1292 py: f32,
1293 cst: &SpatialTree,
1294 child: SpatialNodeIndex,
1295 parent: SpatialNodeIndex,
1296 expected_x: f32,
1297 expected_y: f32,
1298) {
1299 use euclid::approxeq::ApproxEq;
1300 const EPSILON: f32 = 0.0001;
1301
1302 let p = LayoutPoint::new(px, py);
1303 let m = cst.get_relative_transform(child, parent).into_transform();
1304 let pt = m.transform_point2d(p).unwrap();
1305 assert!(pt.x.approx_eq_eps(&expected_x, &EPSILON) &&
1306 pt.y.approx_eq_eps(&expected_y, &EPSILON),
1307 "p: {:?} -> {:?}\nm={:?}",
1308 p, pt, m,
1309 );
1310}
1311
1312#[test]
1313fn test_cst_simple_translation() {
1314 let mut cst = SceneSpatialTree::new();
1317 let root_reference_frame_index = cst.root_reference_frame_index();
1318
1319 let root = add_reference_frame(
1320 &mut cst,
1321 root_reference_frame_index,
1322 LayoutTransform::identity(),
1323 LayoutVector2D::zero(),
1324 );
1325
1326 let child1 = add_reference_frame(
1327 &mut cst,
1328 root,
1329 LayoutTransform::translation(100.0, 0.0, 0.0),
1330 LayoutVector2D::zero(),
1331 );
1332
1333 let child2 = add_reference_frame(
1334 &mut cst,
1335 child1,
1336 LayoutTransform::translation(0.0, 50.0, 0.0),
1337 LayoutVector2D::zero(),
1338 );
1339
1340 let child3 = add_reference_frame(
1341 &mut cst,
1342 child2,
1343 LayoutTransform::translation(200.0, 200.0, 0.0),
1344 LayoutVector2D::zero(),
1345 );
1346
1347 let mut st = SpatialTree::new();
1348 st.apply_updates(cst.end_frame_and_get_pending_updates());
1349 st.update_tree(&SceneProperties::new());
1350
1351 test_pt(100.0, 100.0, &st, child1, root, 200.0, 100.0);
1352 test_pt(100.0, 100.0, &st, child2, root, 200.0, 150.0);
1353 test_pt(100.0, 100.0, &st, child2, child1, 100.0, 150.0);
1354 test_pt(100.0, 100.0, &st, child3, root, 400.0, 350.0);
1355}
1356
1357#[test]
1358fn test_cst_simple_scale() {
1359 let mut cst = SceneSpatialTree::new();
1362 let root_reference_frame_index = cst.root_reference_frame_index();
1363
1364 let root = add_reference_frame(
1365 &mut cst,
1366 root_reference_frame_index,
1367 LayoutTransform::identity(),
1368 LayoutVector2D::zero(),
1369 );
1370
1371 let child1 = add_reference_frame(
1372 &mut cst,
1373 root,
1374 LayoutTransform::scale(4.0, 1.0, 1.0),
1375 LayoutVector2D::zero(),
1376 );
1377
1378 let child2 = add_reference_frame(
1379 &mut cst,
1380 child1,
1381 LayoutTransform::scale(1.0, 2.0, 1.0),
1382 LayoutVector2D::zero(),
1383 );
1384
1385 let child3 = add_reference_frame(
1386 &mut cst,
1387 child2,
1388 LayoutTransform::scale(2.0, 2.0, 1.0),
1389 LayoutVector2D::zero(),
1390 );
1391
1392 let mut st = SpatialTree::new();
1393 st.apply_updates(cst.end_frame_and_get_pending_updates());
1394 st.update_tree(&SceneProperties::new());
1395
1396 test_pt(100.0, 100.0, &st, child1, root, 400.0, 100.0);
1397 test_pt(100.0, 100.0, &st, child2, root, 400.0, 200.0);
1398 test_pt(100.0, 100.0, &st, child3, root, 800.0, 400.0);
1399 test_pt(100.0, 100.0, &st, child2, child1, 100.0, 200.0);
1400 test_pt(100.0, 100.0, &st, child3, child1, 200.0, 400.0);
1401}
1402
1403#[test]
1404fn test_cst_scale_translation() {
1405 let mut cst = SceneSpatialTree::new();
1408 let root_reference_frame_index = cst.root_reference_frame_index();
1409
1410 let root = add_reference_frame(
1411 &mut cst,
1412 root_reference_frame_index,
1413 LayoutTransform::identity(),
1414 LayoutVector2D::zero(),
1415 );
1416
1417 let child1 = add_reference_frame(
1418 &mut cst,
1419 root,
1420 LayoutTransform::translation(100.0, 50.0, 0.0),
1421 LayoutVector2D::zero(),
1422 );
1423
1424 let child2 = add_reference_frame(
1425 &mut cst,
1426 child1,
1427 LayoutTransform::scale(2.0, 4.0, 1.0),
1428 LayoutVector2D::zero(),
1429 );
1430
1431 let child3 = add_reference_frame(
1432 &mut cst,
1433 child2,
1434 LayoutTransform::translation(200.0, -100.0, 0.0),
1435 LayoutVector2D::zero(),
1436 );
1437
1438 let child4 = add_reference_frame(
1439 &mut cst,
1440 child3,
1441 LayoutTransform::scale(3.0, 2.0, 1.0),
1442 LayoutVector2D::zero(),
1443 );
1444
1445 let mut st = SpatialTree::new();
1446 st.apply_updates(cst.end_frame_and_get_pending_updates());
1447 st.update_tree(&SceneProperties::new());
1448
1449 test_pt(100.0, 100.0, &st, child1, root, 200.0, 150.0);
1450 test_pt(100.0, 100.0, &st, child2, root, 300.0, 450.0);
1451 test_pt(100.0, 100.0, &st, child4, root, 1100.0, 450.0);
1452
1453 test_pt(0.0, 0.0, &st, child4, child1, 400.0, -400.0);
1454 test_pt(100.0, 100.0, &st, child4, child1, 1000.0, 400.0);
1455 test_pt(100.0, 100.0, &st, child2, child1, 200.0, 400.0);
1456
1457 test_pt(100.0, 100.0, &st, child3, child1, 600.0, 0.0);
1458}
1459
1460#[test]
1461fn test_cst_translation_rotate() {
1462 use euclid::Angle;
1464
1465 let mut cst = SceneSpatialTree::new();
1466 let root_reference_frame_index = cst.root_reference_frame_index();
1467
1468 let root = add_reference_frame(
1469 &mut cst,
1470 root_reference_frame_index,
1471 LayoutTransform::identity(),
1472 LayoutVector2D::zero(),
1473 );
1474
1475 let child1 = add_reference_frame(
1476 &mut cst,
1477 root,
1478 LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::degrees(-90.0)),
1479 LayoutVector2D::zero(),
1480 );
1481
1482 let mut st = SpatialTree::new();
1483 st.apply_updates(cst.end_frame_and_get_pending_updates());
1484 st.update_tree(&SceneProperties::new());
1485
1486 test_pt(100.0, 0.0, &st, child1, root, 0.0, -100.0);
1487}
1488
1489#[test]
1490fn test_is_ancestor1() {
1491 let mut st = SceneSpatialTree::new();
1492 let root_reference_frame_index = st.root_reference_frame_index();
1493
1494 let root = add_reference_frame(
1495 &mut st,
1496 root_reference_frame_index,
1497 LayoutTransform::identity(),
1498 LayoutVector2D::zero(),
1499 );
1500
1501 let child1_0 = add_reference_frame(
1502 &mut st,
1503 root,
1504 LayoutTransform::identity(),
1505 LayoutVector2D::zero(),
1506 );
1507
1508 let child1_1 = add_reference_frame(
1509 &mut st,
1510 child1_0,
1511 LayoutTransform::identity(),
1512 LayoutVector2D::zero(),
1513 );
1514
1515 let child2 = add_reference_frame(
1516 &mut st,
1517 root,
1518 LayoutTransform::identity(),
1519 LayoutVector2D::zero(),
1520 );
1521
1522 assert!(!st.is_ancestor(root, root));
1523 assert!(!st.is_ancestor(child1_0, child1_0));
1524 assert!(!st.is_ancestor(child1_1, child1_1));
1525 assert!(!st.is_ancestor(child2, child2));
1526
1527 assert!(st.is_ancestor(root, child1_0));
1528 assert!(st.is_ancestor(root, child1_1));
1529 assert!(st.is_ancestor(child1_0, child1_1));
1530
1531 assert!(!st.is_ancestor(child1_0, root));
1532 assert!(!st.is_ancestor(child1_1, root));
1533 assert!(!st.is_ancestor(child1_1, child1_0));
1534
1535 assert!(st.is_ancestor(root, child2));
1536 assert!(!st.is_ancestor(child2, root));
1537
1538 assert!(!st.is_ancestor(child1_0, child2));
1539 assert!(!st.is_ancestor(child1_1, child2));
1540 assert!(!st.is_ancestor(child2, child1_0));
1541 assert!(!st.is_ancestor(child2, child1_1));
1542}
1543
1544#[test]
1546fn test_find_scroll_root_simple() {
1547 let mut st = SceneSpatialTree::new();
1548
1549 let root = st.add_reference_frame(
1550 st.root_reference_frame_index(),
1551 TransformStyle::Flat,
1552 PropertyBinding::Value(LayoutTransform::identity()),
1553 ReferenceFrameKind::Transform {
1554 is_2d_scale_translation: true,
1555 should_snap: true,
1556 paired_with_perspective: false,
1557 },
1558 LayoutVector2D::new(0.0, 0.0),
1559 PipelineId::dummy(),
1560 false,
1561 );
1562
1563 let scroll = st.add_scroll_frame(
1564 root,
1565 ExternalScrollId(1, PipelineId::dummy()),
1566 PipelineId::dummy(),
1567 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1568 &LayoutSize::new(800.0, 400.0),
1569 ScrollFrameKind::Explicit,
1570 LayoutVector2D::new(0.0, 0.0),
1571 APZScrollGeneration::default(),
1572 HasScrollLinkedEffect::No,
1573 );
1574
1575 assert_eq!(st.find_scroll_root(scroll, true), scroll);
1576}
1577
1578#[test]
1580fn test_find_scroll_root_sub_scroll_frame() {
1581 let mut st = SceneSpatialTree::new();
1582
1583 let root = st.add_reference_frame(
1584 st.root_reference_frame_index(),
1585 TransformStyle::Flat,
1586 PropertyBinding::Value(LayoutTransform::identity()),
1587 ReferenceFrameKind::Transform {
1588 is_2d_scale_translation: true,
1589 should_snap: true,
1590 paired_with_perspective: false,
1591 },
1592 LayoutVector2D::new(0.0, 0.0),
1593 PipelineId::dummy(),
1594 false,
1595 );
1596
1597 let root_scroll = st.add_scroll_frame(
1598 root,
1599 ExternalScrollId(1, PipelineId::dummy()),
1600 PipelineId::dummy(),
1601 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1602 &LayoutSize::new(800.0, 400.0),
1603 ScrollFrameKind::Explicit,
1604 LayoutVector2D::new(0.0, 0.0),
1605 APZScrollGeneration::default(),
1606 HasScrollLinkedEffect::No,
1607 );
1608
1609 let sub_scroll = st.add_scroll_frame(
1610 root_scroll,
1611 ExternalScrollId(1, PipelineId::dummy()),
1612 PipelineId::dummy(),
1613 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1614 &LayoutSize::new(800.0, 400.0),
1615 ScrollFrameKind::Explicit,
1616 LayoutVector2D::new(0.0, 0.0),
1617 APZScrollGeneration::default(),
1618 HasScrollLinkedEffect::No,
1619 );
1620
1621 assert_eq!(st.find_scroll_root(sub_scroll, true), root_scroll);
1622}
1623
1624#[test]
1626fn test_find_scroll_root_not_scrollable() {
1627 let mut st = SceneSpatialTree::new();
1628
1629 let root = st.add_reference_frame(
1630 st.root_reference_frame_index(),
1631 TransformStyle::Flat,
1632 PropertyBinding::Value(LayoutTransform::identity()),
1633 ReferenceFrameKind::Transform {
1634 is_2d_scale_translation: true,
1635 should_snap: true,
1636 paired_with_perspective: false,
1637 },
1638 LayoutVector2D::new(0.0, 0.0),
1639 PipelineId::dummy(),
1640 false,
1641 );
1642
1643 let root_scroll = st.add_scroll_frame(
1644 root,
1645 ExternalScrollId(1, PipelineId::dummy()),
1646 PipelineId::dummy(),
1647 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1648 &LayoutSize::new(400.0, 400.0),
1649 ScrollFrameKind::Explicit,
1650 LayoutVector2D::new(0.0, 0.0),
1651 APZScrollGeneration::default(),
1652 HasScrollLinkedEffect::No,
1653 );
1654
1655 let sub_scroll = st.add_scroll_frame(
1656 root_scroll,
1657 ExternalScrollId(1, PipelineId::dummy()),
1658 PipelineId::dummy(),
1659 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1660 &LayoutSize::new(800.0, 400.0),
1661 ScrollFrameKind::Explicit,
1662 LayoutVector2D::new(0.0, 0.0),
1663 APZScrollGeneration::default(),
1664 HasScrollLinkedEffect::No,
1665 );
1666
1667 assert_eq!(st.find_scroll_root(sub_scroll, true), sub_scroll);
1668}
1669
1670#[test]
1672fn test_find_scroll_root_too_small() {
1673 let mut st = SceneSpatialTree::new();
1674
1675 let root = st.add_reference_frame(
1676 st.root_reference_frame_index(),
1677 TransformStyle::Flat,
1678 PropertyBinding::Value(LayoutTransform::identity()),
1679 ReferenceFrameKind::Transform {
1680 is_2d_scale_translation: true,
1681 should_snap: true,
1682 paired_with_perspective: false,
1683 },
1684 LayoutVector2D::new(0.0, 0.0),
1685 PipelineId::dummy(),
1686 false,
1687 );
1688
1689 let root_scroll = st.add_scroll_frame(
1690 root,
1691 ExternalScrollId(1, PipelineId::dummy()),
1692 PipelineId::dummy(),
1693 &LayoutRect::from_size(LayoutSize::new(MIN_SCROLL_ROOT_SIZE, MIN_SCROLL_ROOT_SIZE)),
1694 &LayoutSize::new(1000.0, 1000.0),
1695 ScrollFrameKind::Explicit,
1696 LayoutVector2D::new(0.0, 0.0),
1697 APZScrollGeneration::default(),
1698 HasScrollLinkedEffect::No,
1699 );
1700
1701 let sub_scroll = st.add_scroll_frame(
1702 root_scroll,
1703 ExternalScrollId(1, PipelineId::dummy()),
1704 PipelineId::dummy(),
1705 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1706 &LayoutSize::new(800.0, 400.0),
1707 ScrollFrameKind::Explicit,
1708 LayoutVector2D::new(0.0, 0.0),
1709 APZScrollGeneration::default(),
1710 HasScrollLinkedEffect::No,
1711 );
1712
1713 assert_eq!(st.find_scroll_root(sub_scroll, true), sub_scroll);
1714}
1715
1716#[test]
1719fn test_find_scroll_root_perspective() {
1720 let mut st = SceneSpatialTree::new();
1721
1722 let root = st.add_reference_frame(
1723 st.root_reference_frame_index(),
1724 TransformStyle::Flat,
1725 PropertyBinding::Value(LayoutTransform::identity()),
1726 ReferenceFrameKind::Transform {
1727 is_2d_scale_translation: true,
1728 should_snap: true,
1729 paired_with_perspective: false,
1730 },
1731 LayoutVector2D::new(0.0, 0.0),
1732 PipelineId::dummy(),
1733 false,
1734 );
1735
1736 let root_scroll = st.add_scroll_frame(
1737 root,
1738 ExternalScrollId(1, PipelineId::dummy()),
1739 PipelineId::dummy(),
1740 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1741 &LayoutSize::new(400.0, 400.0),
1742 ScrollFrameKind::Explicit,
1743 LayoutVector2D::new(0.0, 0.0),
1744 APZScrollGeneration::default(),
1745 HasScrollLinkedEffect::No,
1746 );
1747
1748 let perspective = st.add_reference_frame(
1749 root_scroll,
1750 TransformStyle::Flat,
1751 PropertyBinding::Value(LayoutTransform::identity()),
1752 ReferenceFrameKind::Perspective {
1753 scrolling_relative_to: None,
1754 },
1755 LayoutVector2D::new(0.0, 0.0),
1756 PipelineId::dummy(),
1757 false,
1758 );
1759
1760 let sub_scroll = st.add_scroll_frame(
1761 perspective,
1762 ExternalScrollId(1, PipelineId::dummy()),
1763 PipelineId::dummy(),
1764 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1765 &LayoutSize::new(800.0, 400.0),
1766 ScrollFrameKind::Explicit,
1767 LayoutVector2D::new(0.0, 0.0),
1768 APZScrollGeneration::default(),
1769 HasScrollLinkedEffect::No,
1770 );
1771
1772 assert_eq!(st.find_scroll_root(sub_scroll, true), root_scroll);
1773}
1774
1775#[test]
1778fn test_find_scroll_root_2d_scale() {
1779 let mut st = SceneSpatialTree::new();
1780
1781 let root = st.add_reference_frame(
1782 st.root_reference_frame_index(),
1783 TransformStyle::Flat,
1784 PropertyBinding::Value(LayoutTransform::identity()),
1785 ReferenceFrameKind::Transform {
1786 is_2d_scale_translation: true,
1787 should_snap: true,
1788 paired_with_perspective: false,
1789 },
1790 LayoutVector2D::new(0.0, 0.0),
1791 PipelineId::dummy(),
1792 false,
1793 );
1794
1795 let root_scroll = st.add_scroll_frame(
1796 root,
1797 ExternalScrollId(1, PipelineId::dummy()),
1798 PipelineId::dummy(),
1799 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1800 &LayoutSize::new(400.0, 400.0),
1801 ScrollFrameKind::Explicit,
1802 LayoutVector2D::new(0.0, 0.0),
1803 APZScrollGeneration::default(),
1804 HasScrollLinkedEffect::No,
1805 );
1806
1807 let scale = st.add_reference_frame(
1808 root_scroll,
1809 TransformStyle::Flat,
1810 PropertyBinding::Value(LayoutTransform::identity()),
1811 ReferenceFrameKind::Transform {
1812 is_2d_scale_translation: true,
1813 should_snap: false,
1814 paired_with_perspective: false,
1815 },
1816 LayoutVector2D::new(0.0, 0.0),
1817 PipelineId::dummy(),
1818 false,
1819 );
1820
1821 let sub_scroll = st.add_scroll_frame(
1822 scale,
1823 ExternalScrollId(1, PipelineId::dummy()),
1824 PipelineId::dummy(),
1825 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1826 &LayoutSize::new(800.0, 400.0),
1827 ScrollFrameKind::Explicit,
1828 LayoutVector2D::new(0.0, 0.0),
1829 APZScrollGeneration::default(),
1830 HasScrollLinkedEffect::No,
1831 );
1832
1833 assert_eq!(st.find_scroll_root(sub_scroll, true), sub_scroll);
1834}
1835
1836#[test]
1839fn test_find_scroll_root_sticky() {
1840 let mut st = SceneSpatialTree::new();
1841
1842 let root = st.add_reference_frame(
1843 st.root_reference_frame_index(),
1844 TransformStyle::Flat,
1845 PropertyBinding::Value(LayoutTransform::identity()),
1846 ReferenceFrameKind::Transform {
1847 is_2d_scale_translation: true,
1848 should_snap: true,
1849 paired_with_perspective: false,
1850 },
1851 LayoutVector2D::new(0.0, 0.0),
1852 PipelineId::dummy(),
1853 false,
1854 );
1855
1856 let scroll = st.add_scroll_frame(
1857 root,
1858 ExternalScrollId(1, PipelineId::dummy()),
1859 PipelineId::dummy(),
1860 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1861 &LayoutSize::new(400.0, 800.0),
1862 ScrollFrameKind::Explicit,
1863 LayoutVector2D::new(0.0, 0.0),
1864 APZScrollGeneration::default(),
1865 HasScrollLinkedEffect::No,
1866 );
1867
1868 let sticky = st.add_sticky_frame(
1869 scroll,
1870 StickyFrameInfo {
1871 frame_rect: LayoutRect::from_size(LayoutSize::new(400.0, 100.0)),
1872 margins: euclid::SideOffsets2D::new(Some(0.0), None, None, None),
1873 vertical_offset_bounds: api::StickyOffsetBounds::new(0.0, 0.0),
1874 horizontal_offset_bounds: api::StickyOffsetBounds::new(0.0, 0.0),
1875 previously_applied_offset: LayoutVector2D::zero(),
1876 current_offset: LayoutVector2D::zero(),
1877 transform: None
1878 },
1879 PipelineId::dummy(),
1880 );
1881
1882 assert_eq!(st.find_scroll_root(sticky, true), sticky);
1883 assert_eq!(st.find_scroll_root(sticky, false), scroll);
1884}
1885
1886#[test]
1887fn test_world_transforms() {
1888 let mut cst = SceneSpatialTree::new();
1890 let scroll = cst.add_scroll_frame(
1891 cst.root_reference_frame_index(),
1892 ExternalScrollId(1, PipelineId::dummy()),
1893 PipelineId::dummy(),
1894 &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)),
1895 &LayoutSize::new(400.0, 800.0),
1896 ScrollFrameKind::Explicit,
1897 LayoutVector2D::new(0.0, 200.0),
1898 APZScrollGeneration::default(),
1899 HasScrollLinkedEffect::No);
1900
1901 let mut st = SpatialTree::new();
1902 st.apply_updates(cst.end_frame_and_get_pending_updates());
1903 st.update_tree(&SceneProperties::new());
1904
1905 assert_eq!(
1909 st.get_world_transform(scroll).into_transform(),
1910 LayoutToWorldTransform::translation(0.0, -200.0, 0.0));
1911
1912 assert_eq!(
1915 st.get_world_viewport_transform(scroll).into_transform(),
1916 LayoutToWorldTransform::identity());
1917}
1918
1919#[test]
1922fn test_is_ancestor_or_self_zooming() {
1923 let mut cst = SceneSpatialTree::new();
1924 let root_reference_frame_index = cst.root_reference_frame_index();
1925
1926 let root = add_reference_frame(
1927 &mut cst,
1928 root_reference_frame_index,
1929 LayoutTransform::identity(),
1930 LayoutVector2D::zero(),
1931 );
1932 let child1 = add_reference_frame(
1933 &mut cst,
1934 root,
1935 LayoutTransform::identity(),
1936 LayoutVector2D::zero(),
1937 );
1938 let child2 = add_reference_frame(
1939 &mut cst,
1940 child1,
1941 LayoutTransform::identity(),
1942 LayoutVector2D::zero(),
1943 );
1944
1945 let mut st = SpatialTree::new();
1946 st.apply_updates(cst.end_frame_and_get_pending_updates());
1947
1948 st.get_spatial_node_mut(root).is_async_zooming = true;
1950 st.update_tree(&SceneProperties::new());
1951
1952 assert!(st.get_spatial_node(root).is_ancestor_or_self_zooming);
1955 assert!(st.get_spatial_node(child1).is_ancestor_or_self_zooming);
1956 assert!(st.get_spatial_node(child2).is_ancestor_or_self_zooming);
1957}