diff --git a/src/components/main/compositing/quadtree.rs b/src/components/main/compositing/quadtree.rs index 89cd8398405..8c3e426e5b6 100644 --- a/src/components/main/compositing/quadtree.rs +++ b/src/components/main/compositing/quadtree.rs @@ -7,15 +7,25 @@ use geom::point::Point2D; +/// Parent to all quadtree nodes. Stores variables needed at all levels. All method calls +/// at this level are in pixel coordinates. pub struct Quadtree { - tile: Option, - origin: Point2D, - size: f32, - quadrants: [Option<~Quadtree>, ..4], - scale: @mut f32, + root: QuadtreeNode, max_tile_size: uint, } +/// A node in the tree. All method calls at this level are in page coordinates. +struct QuadtreeNode { + /// The tile belonging to this node. Note that parent nodes can have tiles. + tile: Option, + /// The positiong of the node in page coordinates. + origin: Point2D, + /// The width and hight of the node in page coordinates. + size: f32, + /// The node's children. + quadrants: [Option<~QuadtreeNode>, ..4], +} + priv enum Quadrant { TL = 0, TR = 1, @@ -24,13 +34,8 @@ priv enum Quadrant { } impl Quadtree { - // Public method to create a new Quadtree - pub fn new(x: uint, y: uint, width: uint, height: uint, tile_size: uint, scale: @mut f32) -> Quadtree { - if(*scale != 1.0) { - println("Warning: Quadtree: Quadtree initialized while zoomed; this action is unsupported."); - println("Please set zoom to 1.0 before creating the Quadtree."); - } - + /// Public method to create a new Quadtree + pub fn new(x: uint, y: uint, width: uint, height: uint, tile_size: uint) -> Quadtree { // Spaces must be squares and powers of 2, so expand the space until it is let longer = width.max(&height); let num_tiles = uint::div_ceil(longer, tile_size); @@ -38,79 +43,79 @@ impl Quadtree { let size = power_of_two * tile_size; Quadtree { - tile: None, - origin: Point2D(x as f32, y as f32), - size: size as f32, - quadrants: [None, None, None, None], - scale: scale, + root: QuadtreeNode { + tile: None, + origin: Point2D(x as f32, y as f32), + size: size as f32, + quadrants: [None, None, None, None], + }, max_tile_size: tile_size, } } + + /// Get a tile at a given pixel position and scale. + pub fn get_tile<'r>(&'r self, x: uint, y: uint, scale: f32) -> &'r Option { + self.root.get_tile(x as f32 / scale, y as f32 / scale) + } + /// Add a tile associtated with a given pixel position and scale. + pub fn add_tile(&mut self, x: uint, y: uint, scale: f32, tile: T) { + self.root.add_tile(x as f32 / scale, y as f32 / scale, tile, self.max_tile_size as f32 / scale); + } + +} + +impl QuadtreeNode { // Private method to create new children - fn new_child(&self, x: f32, y: f32, size: f32) -> Quadtree { - Quadtree { + fn new_child(x: f32, y: f32, size: f32) -> QuadtreeNode { + QuadtreeNode { tile: None, origin: Point2D(x, y), size: size, quadrants: [None, None, None, None], - scale: self.scale, - max_tile_size: self.max_tile_size, } } - /// Determine which child contains a given point - fn get_quadrant(&self, x: uint, y: uint) -> Quadrant { - let self_x = (self.origin.x * *(self.scale)).ceil() as uint; - let self_y = (self.origin.y * *(self.scale)).ceil() as uint; - let self_size = (self.size * *(self.scale)).ceil() as uint; - - if x < self_x + self_size / 2 { - if y < self_y + self_size / 2 { + /// Determine which child contains a given point in page coords. + fn get_quadrant(&self, x: f32, y: f32) -> Quadrant { + if x < self.origin.x + self.size / 2.0 { + if y < self.origin.y + self.size / 2.0 { TL } else { BL } - } else if y < self_y + self_size / 2 { + } else if y < self.origin.y + self.size / 2.0 { TR } else { BR } } - /// Get the lowest-level (highest resolution) tile associated with a certain pixel - pub fn get_tile<'r> (&'r self, x: uint, y: uint) -> &'r Option { - let self_x = (self.origin.x * *(self.scale)).ceil() as uint; - let self_y = (self.origin.y * *(self.scale)).ceil() as uint; - let self_size = (self.size * *(self.scale)).ceil() as uint; - - if x >= self_x + self_size || x < self_x - || y >= self_y + self_size || y < self_y { + /// Get the lowest-level (highest resolution) tile associated with a given position in page coords. + fn get_tile<'r> (&'r self, x: f32, y: f32) -> &'r Option { + if x >= self.origin.x + self.size || x < self.origin.x + || y >= self.origin.y + self.size || y < self.origin.y { fail!("Quadtree: Tried to get a tile outside of range"); } - let index = self.get_quadrant(x,y) as int; + let index = self.get_quadrant(x, y) as int; match self.quadrants[index] { None => &'r self.tile, Some(ref child) => child.get_tile(x, y), } - } + } - - /// Add a tile - pub fn add_tile(&mut self, x: uint, y: uint, tile: T) { - let self_x = (self.origin.x * *(self.scale)).ceil() as uint; - let self_y = (self.origin.y * *(self.scale)).ceil() as uint; - let self_size = (self.size * *(self.scale)).ceil() as uint; + /// Add a tile associated with a given position in page coords. If the tile size exceeds the maximum, + /// the node will be split and the method will recurse until the tile size is within limits. + fn add_tile(&mut self, x: f32, y: f32, tile: T, tile_size: f32) { + debug!("Quadtree: Adding: (%?, %?) size:%?px", self.origin.x, self.origin.y, self.size); - debug!("Quadtree: Adding: (%?, %?) size:%?px", self_x, self_y, self_size); - - if x >= self_x + self_size || x < self_x - || y >= self_y + self_size || y < self_y { + if x >= self.origin.x + self.size || x < self.origin.x + || y >= self.origin.y + self.size || y < self.origin.y { fail!("Quadtree: Tried to add tile to invalid region"); } - if self_size <= self.max_tile_size { // We are the child + if self.size <= tile_size { // We are the child self.tile = Some(tile); for vec::each([TL, TR, BL, BR]) |quad| { self.quadrants[*quad as int] = None; @@ -118,7 +123,7 @@ impl Quadtree { } else { //send tile to children let quad = self.get_quadrant(x, y); match self.quadrants[quad as int] { - Some(ref mut child) => child.add_tile(x, y, tile), + Some(ref mut child) => child.add_tile(x, y, tile, tile_size), None => { //make new child let new_size = self.size / 2.0; let new_x = match quad { @@ -129,8 +134,8 @@ impl Quadtree { TL | TR => self.origin.y, BL | BR => self.origin.y + new_size, }; - let mut c = ~self.new_child(new_x, new_y, new_size); - c.add_tile(x, y, tile); + let mut c = ~QuadtreeNode::new_child(new_x, new_y, new_size); + c.add_tile(x, y, tile, tile_size); self.quadrants[quad as int] = Some(c); // If we have 4 children, we probably shouldn't be hanging onto a tile @@ -146,23 +151,18 @@ impl Quadtree { } } } - - } - #[test] fn test_add_tile() { - let scale = @mut 1.0; - let mut t = Quadtree::new(50, 30, 20, 20, 10, scale); - assert!(t.get_tile(50, 30).is_none()); - t.add_tile(50, 30, 1); - assert!(t.get_tile(50, 30).get() == 1); - assert!(t.get_tile(59, 39).get() == 1); - assert!(t.get_tile(60, 40).is_none()); - *scale = 2.0; - assert!(t.get_tile(110, 70).get() == 1); - t.add_tile(100, 60, 2); - assert!(t.get_tile(109, 69).get() == 2); - assert!(t.get_tile(110, 70).get() == 1); + let mut t = Quadtree::new(50, 30, 20, 20, 10); + assert!(t.get_tile(50, 30, 1.0).is_none()); + t.add_tile(50, 30, 1.0, 1); + assert!(t.get_tile(50, 30, 1.0).get() == 1); + assert!(t.get_tile(59, 39, 1.0).get() == 1); + assert!(t.get_tile(60, 40, 1.0).is_none()); + assert!(t.get_tile(110, 70, 2.0).get() == 1); + t.add_tile(100, 60, 2.0, 2); + assert!(t.get_tile(109, 69, 2.0).get() == 2); + assert!(t.get_tile(110, 70, 2.0).get() == 1); } \ No newline at end of file