Address pcwalton's comments

This commit is contained in:
eschweic 2013-06-23 15:19:40 -07:00
parent d590a7b45a
commit ad7dc32fc8

View file

@ -7,15 +7,25 @@
use geom::point::Point2D; 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<T> { pub struct Quadtree<T> {
tile: Option<T>, root: QuadtreeNode<T>,
origin: Point2D<f32>,
size: f32,
quadrants: [Option<~Quadtree<T>>, ..4],
scale: @mut f32,
max_tile_size: uint, max_tile_size: uint,
} }
/// A node in the tree. All method calls at this level are in page coordinates.
struct QuadtreeNode<T> {
/// The tile belonging to this node. Note that parent nodes can have tiles.
tile: Option<T>,
/// The positiong of the node in page coordinates.
origin: Point2D<f32>,
/// The width and hight of the node in page coordinates.
size: f32,
/// The node's children.
quadrants: [Option<~QuadtreeNode<T>>, ..4],
}
priv enum Quadrant { priv enum Quadrant {
TL = 0, TL = 0,
TR = 1, TR = 1,
@ -24,13 +34,8 @@ priv enum Quadrant {
} }
impl<T> Quadtree<T> { impl<T> Quadtree<T> {
// Public method to create a new 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<T> { pub fn new(x: uint, y: uint, width: uint, height: uint, tile_size: uint) -> Quadtree<T> {
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.");
}
// Spaces must be squares and powers of 2, so expand the space until it is // Spaces must be squares and powers of 2, so expand the space until it is
let longer = width.max(&height); let longer = width.max(&height);
let num_tiles = uint::div_ceil(longer, tile_size); let num_tiles = uint::div_ceil(longer, tile_size);
@ -38,54 +43,58 @@ impl<T> Quadtree<T> {
let size = power_of_two * tile_size; let size = power_of_two * tile_size;
Quadtree { Quadtree {
root: QuadtreeNode {
tile: None, tile: None,
origin: Point2D(x as f32, y as f32), origin: Point2D(x as f32, y as f32),
size: size as f32, size: size as f32,
quadrants: [None, None, None, None], quadrants: [None, None, None, None],
scale: scale, },
max_tile_size: tile_size, 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<T> {
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<T> QuadtreeNode<T> {
// Private method to create new children // Private method to create new children
fn new_child(&self, x: f32, y: f32, size: f32) -> Quadtree<T> { fn new_child(x: f32, y: f32, size: f32) -> QuadtreeNode<T> {
Quadtree { QuadtreeNode {
tile: None, tile: None,
origin: Point2D(x, y), origin: Point2D(x, y),
size: size, size: size,
quadrants: [None, None, None, None], quadrants: [None, None, None, None],
scale: self.scale,
max_tile_size: self.max_tile_size,
} }
} }
/// Determine which child contains a given point /// Determine which child contains a given point in page coords.
fn get_quadrant(&self, x: uint, y: uint) -> Quadrant { fn get_quadrant(&self, x: f32, y: f32) -> Quadrant {
let self_x = (self.origin.x * *(self.scale)).ceil() as uint; if x < self.origin.x + self.size / 2.0 {
let self_y = (self.origin.y * *(self.scale)).ceil() as uint; if y < self.origin.y + self.size / 2.0 {
let self_size = (self.size * *(self.scale)).ceil() as uint;
if x < self_x + self_size / 2 {
if y < self_y + self_size / 2 {
TL TL
} else { } else {
BL BL
} }
} else if y < self_y + self_size / 2 { } else if y < self.origin.y + self.size / 2.0 {
TR TR
} else { } else {
BR BR
} }
} }
/// Get the lowest-level (highest resolution) tile associated with a certain pixel /// Get the lowest-level (highest resolution) tile associated with a given position in page coords.
pub fn get_tile<'r> (&'r self, x: uint, y: uint) -> &'r Option<T> { fn get_tile<'r> (&'r self, x: f32, y: f32) -> &'r Option<T> {
let self_x = (self.origin.x * *(self.scale)).ceil() as uint; if x >= self.origin.x + self.size || x < self.origin.x
let self_y = (self.origin.y * *(self.scale)).ceil() as uint; || y >= self.origin.y + self.size || y < self.origin.y {
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 {
fail!("Quadtree: Tried to get a tile outside of range"); fail!("Quadtree: Tried to get a tile outside of range");
} }
@ -96,21 +105,17 @@ impl<T> Quadtree<T> {
} }
} }
/// 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);
/// Add a tile if x >= self.origin.x + self.size || x < self.origin.x
pub fn add_tile(&mut self, x: uint, y: uint, tile: T) { || y >= self.origin.y + self.size || y < self.origin.y {
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;
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 {
fail!("Quadtree: Tried to add tile to invalid region"); 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); self.tile = Some(tile);
for vec::each([TL, TR, BL, BR]) |quad| { for vec::each([TL, TR, BL, BR]) |quad| {
self.quadrants[*quad as int] = None; self.quadrants[*quad as int] = None;
@ -118,7 +123,7 @@ impl<T> Quadtree<T> {
} else { //send tile to children } else { //send tile to children
let quad = self.get_quadrant(x, y); let quad = self.get_quadrant(x, y);
match self.quadrants[quad as int] { 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 None => { //make new child
let new_size = self.size / 2.0; let new_size = self.size / 2.0;
let new_x = match quad { let new_x = match quad {
@ -129,8 +134,8 @@ impl<T> Quadtree<T> {
TL | TR => self.origin.y, TL | TR => self.origin.y,
BL | BR => self.origin.y + new_size, BL | BR => self.origin.y + new_size,
}; };
let mut c = ~self.new_child(new_x, new_y, new_size); let mut c = ~QuadtreeNode::new_child(new_x, new_y, new_size);
c.add_tile(x, y, tile); c.add_tile(x, y, tile, tile_size);
self.quadrants[quad as int] = Some(c); self.quadrants[quad as int] = Some(c);
// If we have 4 children, we probably shouldn't be hanging onto a tile // If we have 4 children, we probably shouldn't be hanging onto a tile
@ -146,23 +151,18 @@ impl<T> Quadtree<T> {
} }
} }
} }
} }
#[test] #[test]
fn test_add_tile() { fn test_add_tile() {
let scale = @mut 1.0; let mut t = Quadtree::new(50, 30, 20, 20, 10);
let mut t = Quadtree::new(50, 30, 20, 20, 10, scale); assert!(t.get_tile(50, 30, 1.0).is_none());
assert!(t.get_tile(50, 30).is_none()); t.add_tile(50, 30, 1.0, 1);
t.add_tile(50, 30, 1); assert!(t.get_tile(50, 30, 1.0).get() == 1);
assert!(t.get_tile(50, 30).get() == 1); assert!(t.get_tile(59, 39, 1.0).get() == 1);
assert!(t.get_tile(59, 39).get() == 1); assert!(t.get_tile(60, 40, 1.0).is_none());
assert!(t.get_tile(60, 40).is_none()); assert!(t.get_tile(110, 70, 2.0).get() == 1);
*scale = 2.0; t.add_tile(100, 60, 2.0, 2);
assert!(t.get_tile(110, 70).get() == 1); assert!(t.get_tile(109, 69, 2.0).get() == 2);
t.add_tile(100, 60, 2); assert!(t.get_tile(110, 70, 2.0).get() == 1);
assert!(t.get_tile(109, 69).get() == 2);
assert!(t.get_tile(110, 70).get() == 1);
} }