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