Skip to main content
edited title
Link

binary Binary space partition implemented in Rust which operates on a 2D vector

Make title describe what code does instead of improvement goals per site convention - see https://codereview.stackexchange.com/help/how-to-ask
Link

Refactor a binary space partition in Rust by removing duplicate code, simplifying it, and making it more idiomatic

Source Link

Refactor a binary space partition in Rust by removing duplicate code, simplifying it, and making it more idiomatic

So I have this binary spaced partition leaf:

#[derive(Clone)]
pub struct Leaf {
    container: Rect,
    left: Option<Box<Leaf>>,
    right: Option<Box<Leaf>>,
    room: Option<Rect>,
    split_vertical: Option<bool>,
}

impl Leaf {
    fn new(container: Rect) -> Leaf {
        Leaf {
            container,
            left: None,
            right: None,
            room: None,
            split_vertical: None,
        }
    }

    fn split(
        &mut self,
        grid: Vec<Vec<i32>>,
        mut random_generator: StdRng,
        min_container_size: i32,
        debug_game: bool,
    ) -> bool {
        if self.left.is_some() && self.right.is_some() {
            return false;
        }

        let mut split_vertical: bool = random_generator.gen::<bool>();
        if (self.container.width > self.container.height)
            && ((self.container.width.unwrap() / self.container.height.unwrap()) as f64 >= 1.25)
        {
            split_vertical = true;
        } else if (self.container.height > self.container.width)
            && ((self.container.height.unwrap() / self.container.width.unwrap()) as f64 >= 1.25)
        {
            split_vertical = false;
        }

        let max_size: i32 = if split_vertical {
            self.container.width.unwrap() - min_container_size
        } else {
            self.container.height.unwrap() - min_container_size
        };
        if max_size <= min_container_size {
            return false;
        }

        let mut pos: i32 = random_generator.gen_range(min_container_size..max_size);

        if split_vertical {
            pos += self.container.top_left.x;
            if debug_game {
                println!("split vertical debug");
            }

            self.left = Option::from(Box::new(Leaf {
                container: Rect {
                    top_left: Point {
                        x: self.container.top_left.x,
                        y: self.container.top_left.y,
                    },
                    bottom_right: Point {
                        x: pos - 1,
                        y: self.container.bottom_right.y,
                    },
                    center: None,
                    width: None,
                    height: None,
                },
                left: None,
                right: None,
                room: None,
                split_vertical: None,
            }));
            self.right = Option::from(Box::new(Leaf {
                container: Rect {
                    top_left: Point {
                        x: pos + 1,
                        y: self.container.top_left.y,
                    },
                    bottom_right: Point {
                        x: self.container.bottom_right.x,
                        y: self.container.bottom_right.y,
                    },
                    center: None,
                    width: None,
                    height: None,
                },
                left: None,
                right: None,
                room: None,
                split_vertical: None,
            }))
        } else {
            pos += self.container.top_left.y;
            if debug_game {
                println!("split horizontal debug");
            }

            self.left = Option::from(Box::new(Leaf {
                container: Rect {
                    top_left: Point {
                        x: self.container.top_left.x,
                        y: self.container.top_left.y,
                    },
                    bottom_right: Point {
                        x: self.container.bottom_right.x,
                        y: pos - 1,
                    },
                    center: None,
                    width: None,
                    height: None,
                },
                left: None,
                right: None,
                room: None,
                split_vertical: None,
            }));
            self.right = Option::from(Box::new(Leaf {
                container: Rect {
                    top_left: Point {
                        x: self.container.top_left.x,
                        y: pos - 1,
                    },
                    bottom_right: Point {
                        x: self.container.bottom_right.x,
                        y: self.container.bottom_right.y,
                    },
                    center: None,
                    width: None,
                    height: None,
                },
                left: None,
                right: None,
                room: None,
                split_vertical: None,
            }))
        }

        self.split_vertical = Option::from(split_vertical);

        return true;
    }

    fn create_room(
        &mut self,
        grid: Vec<Vec<i32>>,
        mut random_generator: StdRng,
        min_room_size: i32,
        room_ratio: f32,
    ) -> bool {
        if self.left.is_some() && self.right.is_some() {
            return false;
        }

        let width: i32 = random_generator.gen_range(min_room_size..self.container.width.unwrap());
        let height: i32 = random_generator.gen_range(min_room_size..self.container.height.unwrap());

        let x_pos: i32 = random_generator
            .gen_range(self.container.top_left.x..self.container.bottom_right.x - width);
        let y_pos: i32 = random_generator
            .gen_range(self.container.top_left.y..self.container.bottom_right.y - height);

        let rect: Rect = Rect {
            top_left: Point { x: x_pos, y: y_pos },
            bottom_right: Point {
                x: x_pos + width - 1,
                y: y_pos + height - 1,
            },
            center: None,
            width: None,
            height: None,
        };
        if ((min(rect.width.unwrap(), rect.height.unwrap()) as f32)
            / (max(rect.width.unwrap(), rect.height.unwrap()) as f32))
            < room_ratio
        {
            return false;
        }

        rect.place_rect(grid);
        self.room = Option::from(rect);

        return true;
    }
}

And the Point and Rect structs:

#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub struct Point {
    pub x: i32,
    pub y: i32,
}

impl Point {
    fn new(x: i32, y: i32) -> Point {
        Point { x, y }
    }

    pub fn sum(&self, other: &Point) -> (i32, i32) {
        (self.x + other.x, self.y + other.y)
    }

    pub fn abs_diff(&self, other: &Point) -> (i32, i32) {
        ((self.x - other.x).abs(), (self.y - other.y).abs())
    }
}

#[derive(Clone)]
pub struct Rect {
    pub top_left: Point,
    pub bottom_right: Point,
    pub center: Option<Point>,
    pub width: Option<i32>,
    pub height: Option<i32>,
}

impl Rect {
    fn new(top_left: Point, bottom_right: Point) -> Rect {
        let sum: (i32, i32) = top_left.sum(&bottom_right);
        let diff: (i32, i32) = bottom_right.abs_diff(&top_left);
        Rect {
            top_left,
            bottom_right,
            center: Option::from(Point {
                x: ((sum.0 as f32) / 2.0).round() as i32,
                y: ((sum.1 as f32) / 2.0).round() as i32,
            }),
            width: Option::from(diff.0),
            height: Option::from(diff.1),
        }
    }

    pub fn get_distance_to(&self, other: &Rect) -> i32 {
        return max(
            (self.center.unwrap().x - other.center.unwrap().x).abs(),
            (self.center.unwrap().y - other.center.unwrap().y).abs(),
        );
    }

    pub fn place_rect(&self, grid: Vec<Vec<i32>>) {}
}

However, I'm fairly new to Rust and am not sure how to optimise this further than I already have. I know there's some repetition (especially with creating the child leafs) and wanted to know if there was a way to simplify it, speed it up, and make it more idiomatic?