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 
8 use std::fmt;
9 
10 use euclid::{Transform3D, Box2D, Point2D, Vector2D};
11 
12 use api::units::*;
13 use crate::spatial_tree::{SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace, SpatialNodeContainer};
14 use crate::util::project_rect;
15 use crate::util::{MatrixHelpers, ScaleOffset, RectHelpers, PointHelpers};
16 
17 
18 #[derive(Debug, Clone)]
19 pub 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 
27 impl<F, T> SpaceMapper<F, T> where F: fmt::Debug {
new( ref_spatial_node_index: SpatialNodeIndex, bounds: Box2D<f32, T>, ) -> Self28     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 
new_with_target( ref_spatial_node_index: SpatialNodeIndex, target_node_index: SpatialNodeIndex, bounds: Box2D<f32, T>, spatial_tree: &SpatialTree, ) -> Self41     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 
set_target_spatial_node( &mut self, target_node_index: SpatialNodeIndex, spatial_tree: &SpatialTree, )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 = ref_spatial_node.content_transform
69                 .inverse()
70                 .accumulate(&target_spatial_node.content_transform);
71             CoordinateSpaceMapping::ScaleOffset(scale_offset)
72         } else {
73             let transform = spatial_tree
74                 .get_relative_transform_with_face(
75                     target_node_index,
76                     self.ref_spatial_node_index,
77                     Some(&mut self.visible_face),
78                 )
79                 .into_transform()
80                 .with_source::<F>()
81                 .with_destination::<T>();
82             CoordinateSpaceMapping::Transform(transform)
83         };
84 
85         self.current_target_spatial_node_index = target_node_index;
86     }
87 
get_transform(&self) -> Transform3D<f32, F, T>88     pub fn get_transform(&self) -> Transform3D<f32, F, T> {
89         match self.kind {
90             CoordinateSpaceMapping::Local => {
91                 Transform3D::identity()
92             }
93             CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
94                 scale_offset.to_transform()
95             }
96             CoordinateSpaceMapping::Transform(transform) => {
97                 transform
98             }
99         }
100     }
101 
unmap(&self, rect: &Box2D<f32, T>) -> Option<Box2D<f32, F>>102     pub fn unmap(&self, rect: &Box2D<f32, T>) -> Option<Box2D<f32, F>> {
103         match self.kind {
104             CoordinateSpaceMapping::Local => {
105                 Some(rect.cast_unit())
106             }
107             CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
108                 Some(scale_offset.unmap_rect(rect))
109             }
110             CoordinateSpaceMapping::Transform(ref transform) => {
111                 transform.inverse_rect_footprint(rect)
112             }
113         }
114     }
115 
map(&self, rect: &Box2D<f32, F>) -> Option<Box2D<f32, T>>116     pub fn map(&self, rect: &Box2D<f32, F>) -> Option<Box2D<f32, T>> {
117         match self.kind {
118             CoordinateSpaceMapping::Local => {
119                 Some(rect.cast_unit())
120             }
121             CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
122                 Some(scale_offset.map_rect(rect))
123             }
124             CoordinateSpaceMapping::Transform(ref transform) => {
125                 match project_rect(transform, rect, &self.bounds) {
126                     Some(bounds) => {
127                         Some(bounds)
128                     }
129                     None => {
130                         warn!("parent relative transform can't transform the primitive rect for {:?}", rect);
131                         None
132                     }
133                 }
134             }
135         }
136     }
137 
138     // Attempt to return a rect that is contained in the mapped rect.
map_inner_bounds(&self, rect: &Box2D<f32, F>) -> Option<Box2D<f32, T>>139     pub fn map_inner_bounds(&self, rect: &Box2D<f32, F>) -> Option<Box2D<f32, T>> {
140         match self.kind {
141             CoordinateSpaceMapping::Local => {
142                 Some(rect.cast_unit())
143             }
144             CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
145                 Some(scale_offset.map_rect(rect))
146             }
147             CoordinateSpaceMapping::Transform(..) => {
148                 // We could figure out a rect that is contained in the transformed rect but
149                 // for now we do the simple thing here and bail out.
150                 return None;
151             }
152         }
153     }
154 
map_vector(&self, v: Vector2D<f32, F>) -> Vector2D<f32, T>155     pub fn map_vector(&self, v: Vector2D<f32, F>) -> Vector2D<f32, T> {
156         match self.kind {
157             CoordinateSpaceMapping::Local => {
158                 v.cast_unit()
159             }
160             CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => {
161                 scale_offset.map_vector(&v)
162             }
163             CoordinateSpaceMapping::Transform(ref transform) => {
164                 transform.transform_vector2d(v)
165             }
166         }
167     }
168 }
169 
170 
171 #[derive(Clone, Debug)]
172 pub struct SpaceSnapper {
173     ref_spatial_node_index: SpatialNodeIndex,
174     current_target_spatial_node_index: SpatialNodeIndex,
175     snapping_transform: Option<ScaleOffset>,
176     raster_pixel_scale: RasterPixelScale,
177 }
178 
179 impl SpaceSnapper {
new( ref_spatial_node_index: SpatialNodeIndex, raster_pixel_scale: RasterPixelScale, ) -> Self180     pub fn new(
181         ref_spatial_node_index: SpatialNodeIndex,
182         raster_pixel_scale: RasterPixelScale,
183     ) -> Self {
184         SpaceSnapper {
185             ref_spatial_node_index,
186             current_target_spatial_node_index: SpatialNodeIndex::INVALID,
187             snapping_transform: None,
188             raster_pixel_scale,
189         }
190     }
191 
new_with_target<S: SpatialNodeContainer>( ref_spatial_node_index: SpatialNodeIndex, target_node_index: SpatialNodeIndex, raster_pixel_scale: RasterPixelScale, spatial_tree: &S, ) -> Self192     pub fn new_with_target<S: SpatialNodeContainer>(
193         ref_spatial_node_index: SpatialNodeIndex,
194         target_node_index: SpatialNodeIndex,
195         raster_pixel_scale: RasterPixelScale,
196         spatial_tree: &S,
197     ) -> Self {
198         let mut snapper = 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         snapper.set_target_spatial_node(target_node_index, spatial_tree);
206         snapper
207     }
208 
set_target_spatial_node<S: SpatialNodeContainer>( &mut self, target_node_index: SpatialNodeIndex, spatial_tree: &S, )209     pub fn set_target_spatial_node<S: SpatialNodeContainer>(
210         &mut self,
211         target_node_index: SpatialNodeIndex,
212         spatial_tree: &S,
213     ) {
214         if target_node_index == self.current_target_spatial_node_index {
215             return
216         }
217 
218         let ref_snap = spatial_tree.get_node_info(self.ref_spatial_node_index).snapping_transform;
219         let target_snap = spatial_tree.get_node_info(target_node_index).snapping_transform;
220 
221         self.current_target_spatial_node_index = target_node_index;
222         self.snapping_transform = match (ref_snap, target_snap) {
223             (Some(ref ref_scale_offset), Some(ref target_scale_offset)) => {
224                 Some(ref_scale_offset
225                     .inverse()
226                     .accumulate(target_scale_offset)
227                     .scale(self.raster_pixel_scale.0))
228             }
229             _ => None,
230         };
231     }
232 
snap_rect<F>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, F> where F: fmt::Debug233     pub fn snap_rect<F>(&self, rect: &Box2D<f32, F>) -> Box2D<f32, F> where F: fmt::Debug {
234         debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID);
235         match self.snapping_transform {
236             Some(ref scale_offset) => {
237                 let snapped_device_rect: DeviceRect = scale_offset.map_rect(rect).snap();
238                 scale_offset.unmap_rect(&snapped_device_rect)
239             }
240             None => *rect,
241         }
242     }
243 
snap_point<F>(&self, point: &Point2D<f32, F>) -> Point2D<f32, F> where F: fmt::Debug244     pub fn snap_point<F>(&self, point: &Point2D<f32, F>) -> Point2D<f32, F> where F: fmt::Debug {
245         debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID);
246         match self.snapping_transform {
247             Some(ref scale_offset) => {
248                 let snapped_device_vector : DevicePoint = scale_offset.map_point(point).snap();
249                 scale_offset.unmap_point(&snapped_device_vector)
250             }
251             None => *point,
252         }
253     }
254 }
255