/* * Copyright (C) 2014 Alfons Wirtz * website www.freerouting.net * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License at <http://www.gnu.org/licenses/> * for more details. * * MinAreaTree.java * * Created on 1. September 2004, 08:29 */ package datastructures; import java.util.Set; import java.util.TreeSet; import geometry.planar.ShapeBoundingDirections; import geometry.planar.RegularTileShape; /** * Binary search tree for shapes in the plane. * The shapes are stored in the leafs of the tree. * The algorithm for storing a new shape is as following. * Starting from the root go to the child, so that the increase of the bounding shape of that child * is minimal after adding the new shape, until you reach a leaf. * The use of ShapeDirections to calculate the bounding shape is for historical reasons (coming from a Kd-Tree). * Instead any algorithm to calculate a bounding shape of two input shapes can be used. * The algorithm would of course also work for higher dimensions. * * @author Alfons Wirtz */ public class MinAreaTree extends ShapeTree { /** * Constructor with a fixed set of directions defining the keys and and * the surrounding shapes */ public MinAreaTree(ShapeBoundingDirections p_directions) { super(p_directions); } /** * Calculates the objects in this tree, which overlap with p_shape */ public Set<Leaf> overlaps(RegularTileShape p_shape) { Set<Leaf> found_overlaps = new TreeSet<Leaf>() ; if (this.root == null) { return found_overlaps; } this.node_stack.reset(); this.node_stack.push(this.root); TreeNode curr_node; for (;;) { curr_node = this.node_stack.pop(); if (curr_node == null) { break; } if (curr_node.bounding_shape.intersects(p_shape)) { if (curr_node instanceof Leaf) { found_overlaps.add((Leaf) curr_node); } else { this.node_stack.push(((InnerNode)curr_node).first_child); this.node_stack.push(((InnerNode)curr_node).second_child); } } } return found_overlaps ; } void insert(Leaf p_leaf) { ++this.leaf_count; // Tree is empty - just insert the new leaf if ( root == null ) { root = p_leaf ; return; } // Non-empty tree - do a recursive location for leaf replacement Leaf leaf_to_replace = position_locate(root, p_leaf) ; // Construct a new node - whenever a leaf is added so is a new node RegularTileShape new_bounds = p_leaf.bounding_shape.union(leaf_to_replace.bounding_shape) ; InnerNode curr_parent = leaf_to_replace.parent; InnerNode new_node = new InnerNode(new_bounds, curr_parent) ; if ( leaf_to_replace.parent!= null ) { // Replace the pointer from the parent to the leaf with our new node if ( leaf_to_replace == curr_parent.first_child ) { curr_parent.first_child = new_node ; } else { curr_parent.second_child = new_node ; } } // Update the parent pointers of the old leaf and new leaf to point to new node leaf_to_replace.parent = new_node ; p_leaf.parent = new_node ; // Insert the children in any order. new_node.first_child = leaf_to_replace ; new_node.second_child = p_leaf ; if (root == leaf_to_replace) { root = new_node; } } private final Leaf position_locate(TreeNode p_curr_node, Leaf p_leaf_to_insert) { TreeNode curr_node = p_curr_node; while (!(curr_node instanceof Leaf)) { InnerNode curr_inner_node = (InnerNode) curr_node; curr_inner_node.bounding_shape = p_leaf_to_insert.bounding_shape.union(curr_inner_node.bounding_shape) ; // Choose the the child, so that the area increase of that child after taking the union // with the shape of p_leaf_to_insert is minimal. RegularTileShape first_child_shape = curr_inner_node.first_child.bounding_shape; RegularTileShape union_with_first_child_shape = p_leaf_to_insert.bounding_shape.union(first_child_shape); double first_area_increase = union_with_first_child_shape.area()- first_child_shape.area(); RegularTileShape second_child_shape = curr_inner_node.second_child.bounding_shape; RegularTileShape union_with_second_child_shape = p_leaf_to_insert.bounding_shape.union(second_child_shape); double second_area_increase = union_with_second_child_shape.area() - second_child_shape.area(); if (first_area_increase <= second_area_increase) { curr_node = curr_inner_node.first_child ; } else { curr_node = curr_inner_node.second_child ; } } return (Leaf) curr_node; } /** * removes an entry from this tree */ public void remove_leaf(Leaf p_leaf) { if (p_leaf == null) { return; } // remove the leaf node InnerNode parent = p_leaf.parent; p_leaf.bounding_shape = null; p_leaf.parent = null; p_leaf.object = null; --this.leaf_count; if (parent == null) { // tree gets empty root = null; return; } // find the other leaf of the parent TreeNode other_leaf; if (parent.second_child == p_leaf) { other_leaf = parent.first_child; } else if (parent.first_child == p_leaf) { other_leaf = parent.second_child; } else { System.out.println("MinAreaTree.remove_leaf: parent inconsistent"); other_leaf = null; } // link the other leaf to the grand_parent and remove the parent node InnerNode grand_parent = parent.parent; other_leaf.parent = grand_parent; if (grand_parent == null) { // only one leaf left in the tree root = other_leaf; } else { if (grand_parent.second_child == parent) { grand_parent.second_child = other_leaf; } else if (grand_parent.first_child == parent) { grand_parent.first_child = other_leaf; } else { System.out.println("MinAreaTree.remove_leaf: grand_parent inconsistent"); } } parent.parent = null; parent.first_child = null; parent.second_child = null; parent.bounding_shape = null; // recalculate the bounding shapes of the ancestors // as long as it gets smaller after removing p_leaf InnerNode node_to_recalculate = grand_parent; while (node_to_recalculate != null) { RegularTileShape new_bounds = node_to_recalculate.second_child.bounding_shape.union(node_to_recalculate.first_child.bounding_shape); if (new_bounds.contains(node_to_recalculate.bounding_shape)) { // the new bounds are not smaller, no further recalculate nessesary break; } node_to_recalculate.bounding_shape = new_bounds; node_to_recalculate = node_to_recalculate.parent; } } protected ArrayStack<TreeNode> node_stack = new ArrayStack<TreeNode> (10000); }