Skip to main content

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    pub fn as_2d_scale_offset(&self) -> Option<ScaleOffset> {
184        self.kind.as_2d_scale_offset()
185    }
186}
187
188
189#[derive(Clone, Debug)]
190pub struct SpaceSnapper {
191    ref_spatial_node_index: SpatialNodeIndex,
192    current_target_spatial_node_index: SpatialNodeIndex,
193    snapping_transform: Option<ScaleOffset>,
194    raster_pixel_scale: RasterPixelScale,
195}
196
197impl SpaceSnapper {
198    pub fn new(
199        ref_spatial_node_index: SpatialNodeIndex,
200        raster_pixel_scale: RasterPixelScale,
201    ) -> Self {
202        SpaceSnapper {
203            ref_spatial_node_index,
204            current_target_spatial_node_index: SpatialNodeIndex::INVALID,
205            snapping_transform: None,
206            raster_pixel_scale,
207        }
208    }
209
210    pub fn set_target_spatial_node<S: SpatialNodeContainer>(
211        &mut self,
212        target_node_index: SpatialNodeIndex,
213        spatial_tree: &S,
214    ) {
215        if target_node_index == self.current_target_spatial_node_index {
216            return
217        }
218
219        let ref_snap = spatial_tree.get_node_info(self.ref_spatial_node_index).snapping_transform;
220        let target_snap = spatial_tree.get_node_info(target_node_index).snapping_transform;
221
222        self.current_target_spatial_node_index = target_node_index;
223        self.snapping_transform = match (ref_snap, target_snap) {
224            (Some(ref ref_scale_offset), Some(ref target_scale_offset)) => {
225                Some(target_scale_offset
226                    .pre_scale(self.raster_pixel_scale.0)
227                    .then(&ref_scale_offset.inverse())
228                )
229            }
230            _ => None,
231        };
232    }
233
234    pub fn snap_rect<F>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, F> where F: fmt::Debug {
235        debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID);
236        match self.snapping_transform {
237            Some(ref scale_offset) => {
238                let snapped_device_rect: DeviceRect = scale_offset.map_rect(rect).snap();
239                scale_offset.unmap_rect(&snapped_device_rect)
240            }
241            None => *rect,
242        }
243    }
244
245    pub fn snap_point<F>(&self, point: &Point2D<f32, F>) -> Point2D<f32, F> where F: fmt::Debug {
246        debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID);
247        match self.snapping_transform {
248            Some(ref scale_offset) => {
249                let snapped_device_vector: DevicePoint = scale_offset.map_point(point).snap();
250                scale_offset.unmap_point(&snapped_device_vector)
251            }
252            None => *point,
253        }
254    }
255}