space.rs (9499B)
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 { 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)] 186 pub 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 193 impl 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 }