webrender/
space.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
6//! Utilities to deal with coordinate spaces.
7
8use std::fmt;
9
10use euclid::{Transform3D, Box2D, Point2D, Vector2D};
11
12use api::units::*;
13use crate::spatial_tree::{SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace, SpatialNodeContainer};
14use crate::util::project_rect;
15use crate::util::{MatrixHelpers, ScaleOffset, RectHelpers, PointHelpers};
16
17
18#[derive(Debug, Clone)]
19pub struct SpaceMapper<F, T> {
20    kind: CoordinateSpaceMapping<F, T>,
21    pub ref_spatial_node_index: SpatialNodeIndex,
22    pub current_target_spatial_node_index: SpatialNodeIndex,
23    pub bounds: Box2D<f32, T>,
24    visible_face: VisibleFace,
25}
26
27impl<F, T> SpaceMapper<F, T> where F: fmt::Debug {
28    pub fn new(
29        ref_spatial_node_index: SpatialNodeIndex,
30        bounds: Box2D<f32, T>,
31    ) -> Self {
32        SpaceMapper {
33            kind: CoordinateSpaceMapping::Local,
34            ref_spatial_node_index,
35            current_target_spatial_node_index: ref_spatial_node_index,
36            bounds,
37            visible_face: VisibleFace::Front,
38        }
39    }
40
41    pub fn new_with_target(
42        ref_spatial_node_index: SpatialNodeIndex,
43        target_node_index: SpatialNodeIndex,
44        bounds: Box2D<f32, T>,
45        spatial_tree: &SpatialTree,
46    ) -> Self {
47        let mut mapper = Self::new(ref_spatial_node_index, bounds);
48        mapper.set_target_spatial_node(target_node_index, spatial_tree);
49        mapper
50    }
51
52    pub fn set_target_spatial_node(
53        &mut self,
54        target_node_index: SpatialNodeIndex,
55        spatial_tree: &SpatialTree,
56    ) {
57        if target_node_index == self.current_target_spatial_node_index {
58            return
59        }
60
61        let ref_spatial_node = spatial_tree.get_spatial_node(self.ref_spatial_node_index);
62        let target_spatial_node = spatial_tree.get_spatial_node(target_node_index);
63        self.visible_face = VisibleFace::Front;
64
65        self.kind = if self.ref_spatial_node_index == target_node_index {
66            CoordinateSpaceMapping::Local
67        } else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id {
68            let scale_offset = target_spatial_node.content_transform
69                .then(&ref_spatial_node.content_transform.inverse());
70            CoordinateSpaceMapping::ScaleOffset(scale_offset)
71        } else {
72            let transform = spatial_tree
73                .get_relative_transform_with_face(
74                    target_node_index,
75                    self.ref_spatial_node_index,
76                    Some(&mut self.visible_face),
77                )
78                .into_transform()
79                .with_source::<F>()
80                .with_destination::<T>();
81            CoordinateSpaceMapping::Transform(transform)
82        };
83
84        self.current_target_spatial_node_index = target_node_index;
85    }
86
87    pub fn get_transform(&self) -> Transform3D<f32, F, T> {
88        match self.kind {
89            CoordinateSpaceMapping::Local => {
90                Transform3D::identity()
91            }
92            CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
93                scale_offset.to_transform()
94            }
95            CoordinateSpaceMapping::Transform(transform) => {
96                transform
97            }
98        }
99    }
100
101    pub fn unmap(&self, rect: &Box2D<f32, T>) -> Option<Box2D<f32, F>> {
102        match self.kind {
103            CoordinateSpaceMapping::Local => {
104                Some(rect.cast_unit())
105            }
106            CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
107                Some(scale_offset.unmap_rect(rect))
108            }
109            CoordinateSpaceMapping::Transform(ref transform) => {
110                transform.inverse_rect_footprint(rect)
111            }
112        }
113    }
114
115    pub fn map(&self, rect: &Box2D<f32, F>) -> Option<Box2D<f32, T>> {
116        match self.kind {
117            CoordinateSpaceMapping::Local => {
118                Some(rect.cast_unit())
119            }
120            CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
121                Some(scale_offset.map_rect(rect))
122            }
123            CoordinateSpaceMapping::Transform(ref transform) => {
124                match project_rect(transform, rect, &self.bounds) {
125                    Some(bounds) => {
126                        Some(bounds)
127                    }
128                    None => {
129                        warn!("parent relative transform can't transform the primitive rect for {:?}", rect);
130                        None
131                    }
132                }
133            }
134        }
135    }
136
137    // Attempt to return a rect that is contained in the mapped rect.
138    pub fn map_inner_bounds(&self, rect: &Box2D<f32, F>) -> Option<Box2D<f32, T>> {
139        match self.kind {
140            CoordinateSpaceMapping::Local => {
141                Some(rect.cast_unit())
142            }
143            CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
144                Some(scale_offset.map_rect(rect))
145            }
146            CoordinateSpaceMapping::Transform(..) => {
147                // We could figure out a rect that is contained in the transformed rect but
148                // for now we do the simple thing here and bail out.
149                return None;
150            }
151        }
152    }
153
154    // Map a local space point to the target coordinate space
155    pub fn map_point(&self, p: Point2D<f32, F>) -> Option<Point2D<f32, T>> {
156        match self.kind {
157            CoordinateSpaceMapping::Local => {
158                Some(p.cast_unit())
159            }
160            CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
161                Some(scale_offset.map_point(&p))
162            }
163            CoordinateSpaceMapping::Transform(ref transform) => {
164                transform.transform_point2d(p)
165            }
166        }
167    }
168
169    pub fn map_vector(&self, v: Vector2D<f32, F>) -> Vector2D<f32, T> {
170        match self.kind {
171            CoordinateSpaceMapping::Local => {
172                v.cast_unit()
173            }
174            CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
175                scale_offset.map_vector(&v)
176            }
177            CoordinateSpaceMapping::Transform(ref transform) => {
178                transform.transform_vector2d(v)
179            }
180        }
181    }
182}
183
184
185#[derive(Clone, Debug)]
186pub struct SpaceSnapper {
187    ref_spatial_node_index: SpatialNodeIndex,
188    current_target_spatial_node_index: SpatialNodeIndex,
189    snapping_transform: Option<ScaleOffset>,
190    raster_pixel_scale: RasterPixelScale,
191}
192
193impl SpaceSnapper {
194    pub fn new(
195        ref_spatial_node_index: SpatialNodeIndex,
196        raster_pixel_scale: RasterPixelScale,
197    ) -> Self {
198        SpaceSnapper {
199            ref_spatial_node_index,
200            current_target_spatial_node_index: SpatialNodeIndex::INVALID,
201            snapping_transform: None,
202            raster_pixel_scale,
203        }
204    }
205
206    pub fn new_with_target<S: SpatialNodeContainer>(
207        ref_spatial_node_index: SpatialNodeIndex,
208        target_node_index: SpatialNodeIndex,
209        raster_pixel_scale: RasterPixelScale,
210        spatial_tree: &S,
211    ) -> Self {
212        let mut snapper = SpaceSnapper {
213            ref_spatial_node_index,
214            current_target_spatial_node_index: SpatialNodeIndex::INVALID,
215            snapping_transform: None,
216            raster_pixel_scale,
217        };
218
219        snapper.set_target_spatial_node(target_node_index, spatial_tree);
220        snapper
221    }
222
223    pub fn set_target_spatial_node<S: SpatialNodeContainer>(
224        &mut self,
225        target_node_index: SpatialNodeIndex,
226        spatial_tree: &S,
227    ) {
228        if target_node_index == self.current_target_spatial_node_index {
229            return
230        }
231
232        let ref_snap = spatial_tree.get_node_info(self.ref_spatial_node_index).snapping_transform;
233        let target_snap = spatial_tree.get_node_info(target_node_index).snapping_transform;
234
235        self.current_target_spatial_node_index = target_node_index;
236        self.snapping_transform = match (ref_snap, target_snap) {
237            (Some(ref ref_scale_offset), Some(ref target_scale_offset)) => {
238                Some(target_scale_offset
239                    .pre_scale(self.raster_pixel_scale.0)
240                    .then(&ref_scale_offset.inverse())
241                )
242            }
243            _ => None,
244        };
245    }
246
247    pub fn snap_rect<F>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, F> where F: fmt::Debug {
248        debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID);
249        match self.snapping_transform {
250            Some(ref scale_offset) => {
251                let snapped_device_rect: DeviceRect = scale_offset.map_rect(rect).snap();
252                scale_offset.unmap_rect(&snapped_device_rect)
253            }
254            None => *rect,
255        }
256    }
257
258    pub fn snap_point<F>(&self, point: &Point2D<f32, F>) -> Point2D<f32, F> where F: fmt::Debug {
259        debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID);
260        match self.snapping_transform {
261            Some(ref scale_offset) => {
262                let snapped_device_vector : DevicePoint = scale_offset.map_point(point).snap();
263                scale_offset.unmap_point(&snapped_device_vector)
264            }
265            None => *point,
266        }
267    }
268}