/*
* 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.
*
* BoxShapeSearchTree.java
*
* Created on 20. Mai 2007, 07:33
*
*/
package board;
import java.util.Collection;
import java.util.LinkedList;
import geometry.planar.OrthogonalBoundingDirections;
import geometry.planar.TileShape;
import geometry.planar.Shape;
import geometry.planar.IntBox;
import geometry.planar.Polyline;
import autoroute.IncompleteFreeSpaceExpansionRoom;
import autoroute.CompleteFreeSpaceExpansionRoom;
/**
* A special simple ShapeSearchtree, where the shapes are of class IntBox.
* It is used in the 90-degree autorouter algorithm.
*
* @author Alfons Wirtz
*/
public class ShapeSearchTree90Degree extends ShapeSearchTree
{
/** Creates a new instance of ShapeSearchTree90Degree */
public ShapeSearchTree90Degree(BasicBoard p_board, int p_compensated_clearance_class_no)
{
super(OrthogonalBoundingDirections.INSTANCE, p_board, p_compensated_clearance_class_no);
}
/**
* Calculates a new incomplete room with a maximal TileShape contained in the shape of p_room,
* which may overlap only with items of the input net on the input layer.
* p_room.get_contained_shape() will be contained in the shape of the result room.
* If that is not possible, several rooms are returned with shapes,
* which intersect with p_room.get_contained_shape().
* The result room is not yet complete, because its doors are not yet calculated.
*/
public Collection<IncompleteFreeSpaceExpansionRoom> complete_shape(IncompleteFreeSpaceExpansionRoom p_room,
int p_net_no, SearchTreeObject p_ignore_object, TileShape p_ignore_shape)
{
if (!(p_room.get_contained_shape() instanceof IntBox))
{
System.out.println("BoxShapeSearchTree.complete_shape: unexpected p_shape_to_be_contained");
return new LinkedList<IncompleteFreeSpaceExpansionRoom>();
}
IntBox shape_to_be_contained = (IntBox) p_room.get_contained_shape();
if (this.root == null)
{
return new LinkedList<IncompleteFreeSpaceExpansionRoom>();
}
IntBox start_shape = board.get_bounding_box();
if (p_room.get_shape() != null)
{
if (!(p_room.get_shape() instanceof IntBox))
{
System.out.println("BoxShapeSearchTree.complete_shape: p_start_shape of type IntBox expected");
return new LinkedList<IncompleteFreeSpaceExpansionRoom>();
}
start_shape = ((IntBox)p_room.get_shape()).intersection(start_shape);
}
IntBox bounding_shape = start_shape;
int room_layer = p_room.get_layer();
Collection<IncompleteFreeSpaceExpansionRoom> result = new LinkedList<IncompleteFreeSpaceExpansionRoom>();
result.add(new IncompleteFreeSpaceExpansionRoom(start_shape, room_layer, shape_to_be_contained));
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(bounding_shape))
{
if (curr_node instanceof Leaf)
{
Leaf curr_leaf = (Leaf) curr_node;
SearchTreeObject curr_object = (SearchTreeObject) curr_leaf.object;
int shape_index = curr_leaf.shape_index_in_object;
if (curr_object.is_trace_obstacle(p_net_no) && curr_object.shape_layer(shape_index) == room_layer
&& curr_object != p_ignore_object)
{
IntBox curr_object_shape = curr_object.get_tree_shape(this, shape_index).bounding_box();
Collection<IncompleteFreeSpaceExpansionRoom> new_result = new LinkedList<IncompleteFreeSpaceExpansionRoom>();
IntBox new_bounding_shape = IntBox.EMPTY;
for (IncompleteFreeSpaceExpansionRoom curr_room : result)
{
IntBox curr_shape = (IntBox) curr_room.get_shape();
if (curr_shape.overlaps(curr_object_shape))
{
if (curr_object instanceof CompleteFreeSpaceExpansionRoom
&& p_ignore_shape != null )
{
IntBox intersection = curr_shape.intersection(curr_object_shape);
if (p_ignore_shape.contains(intersection))
{
// ignore also all objects, whose intersection is contained in the
// 2-dim overlap-door with the from_room.
continue;
}
}
Collection <IncompleteFreeSpaceExpansionRoom> new_restrained_shapes =
restrain_shape(curr_room, curr_object_shape);
new_result.addAll(new_restrained_shapes);
for (IncompleteFreeSpaceExpansionRoom tmp_shape : new_result)
{
new_bounding_shape = new_bounding_shape.union(tmp_shape.get_shape().bounding_box());
}
}
else
{
new_result.add(curr_room);
new_bounding_shape = new_bounding_shape.union(curr_shape.bounding_box());
}
}
result = new_result;
bounding_shape = new_bounding_shape;
}
}
else
{
this.node_stack.push(((InnerNode)curr_node).first_child);
this.node_stack.push(((InnerNode)curr_node).second_child);
}
}
}
return result;
}
/**
* Restrains the shape of p_incomplete_room to a box shape, which does not intersect with the interiour
* of p_obstacle_shape. p_incomplete_room.get_contained_shape() must be contained in the shape of
* the result room.
*/
private Collection<IncompleteFreeSpaceExpansionRoom> restrain_shape(IncompleteFreeSpaceExpansionRoom p_incomplete_room, IntBox p_obstacle_shape)
{
// Search the edge line of p_obstacle_shape, so that p_shape_to_be_contained
// are on the right side of this line, and that the line segment
// intersects with the interiour of p_shape.
// If there are more than 1 such lines take the line which is
// furthest away from the shape_to_be_contained
// Then insersect p_shape with the halfplane defined by the
// opposite of this line.
Collection<IncompleteFreeSpaceExpansionRoom> result = new LinkedList<IncompleteFreeSpaceExpansionRoom>();
if (p_incomplete_room.get_contained_shape().is_empty())
{
if (this.board.get_test_level().ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal())
{
System.out.println("BoxShapeSearchTree.restrain_shape: p_shape_to_be_contained is empty");
}
return result;
}
IntBox room_shape = p_incomplete_room.get_shape().bounding_box();
IntBox shape_to_be_contained = p_incomplete_room.get_contained_shape().bounding_box();
int cut_line_distance = 0;
IntBox restrained_shape = null;
if (room_shape.ll.x < p_obstacle_shape.ur.x && room_shape.ur.x > p_obstacle_shape.ur.x
&& room_shape.ur.y > p_obstacle_shape.ll.y && room_shape.ll.y < p_obstacle_shape.ur.y )
{
// The right line segment of the obstacle_shape intersects the interiour of p_shape
int curr_distance = shape_to_be_contained.ll.x - p_obstacle_shape.ur.x;
if (curr_distance > cut_line_distance)
{
cut_line_distance = curr_distance;
restrained_shape = new IntBox(p_obstacle_shape.ur.x, room_shape.ll.y, room_shape.ur.x, room_shape.ur.y);
}
}
if (room_shape.ll.x < p_obstacle_shape.ll.x && room_shape.ur.x > p_obstacle_shape.ll.x
&& room_shape.ur.y > p_obstacle_shape.ll.y && room_shape.ll.y < p_obstacle_shape.ur.y )
{
// The left line segment of the obstacle_shape intersects the interiour of p_shape
int curr_distance = p_obstacle_shape.ll.x - shape_to_be_contained.ur.x;
if (curr_distance > cut_line_distance)
{
cut_line_distance = curr_distance;
restrained_shape = new IntBox(room_shape.ll.x, room_shape.ll.y, p_obstacle_shape.ll.x, room_shape.ur.y);
}
}
if (room_shape.ll.y < p_obstacle_shape.ll.y && room_shape.ur.y > p_obstacle_shape.ll.y
&& room_shape.ur.x > p_obstacle_shape.ll.x && room_shape.ll.x < p_obstacle_shape.ur.x )
{
// The lower line segment of the obstacle_shape intersects the interiour of p_shape
int curr_distance = p_obstacle_shape.ll.y - shape_to_be_contained.ur.y;
if (curr_distance > cut_line_distance)
{
cut_line_distance = curr_distance;
restrained_shape = new IntBox(room_shape.ll.x, room_shape.ll.y, room_shape.ur.x, p_obstacle_shape.ll.y);
}
}
if (room_shape.ll.y < p_obstacle_shape.ur.y && room_shape.ur.y > p_obstacle_shape.ur.y
&& room_shape.ur.x > p_obstacle_shape.ll.x && room_shape.ll.x < p_obstacle_shape.ur.x )
{
// The upper line segment of the obstacle_shape intersects the interiour of p_shape
int curr_distance = shape_to_be_contained.ll.y - p_obstacle_shape.ur.y;
if (curr_distance > cut_line_distance)
{
cut_line_distance = curr_distance;
restrained_shape = new IntBox(room_shape.ll.x, p_obstacle_shape.ur.y, room_shape.ur.x, room_shape.ur.y);
}
}
if (restrained_shape != null)
{
result.add(new IncompleteFreeSpaceExpansionRoom(restrained_shape,
p_incomplete_room.get_layer(), shape_to_be_contained));
return result;
}
// Now shape_to_be_contained intersects with the obstacle_shape.
// shape_to_be_contained and p_shape evtl. need to be divided in two.
IntBox is = shape_to_be_contained.intersection(p_obstacle_shape);
if (is.is_empty())
{
System.out.println("BoxShapeSearchTree.restrain_shape: Intersection between obstacle_shape and shape_to_be_contained expected");
return result;
}
IntBox new_shape_1 = null;
IntBox new_shape_2 = null;
if (is.ll.x > room_shape.ll.x && is.ll.x == p_obstacle_shape.ll.x && is.ll.x < room_shape.ur.x)
{
new_shape_1 = new IntBox(room_shape.ll.x, room_shape.ll.y, is.ll.x, room_shape.ur.y);
new_shape_2 = new IntBox(is.ll.x, room_shape.ll.y, room_shape.ur.x, room_shape.ur.y);
}
else if (is.ur.x > room_shape.ll.x && is.ur.x == p_obstacle_shape.ur.x && is.ur.x < room_shape.ur.x)
{
new_shape_2 = new IntBox(room_shape.ll.x, room_shape.ll.y, is.ur.x, room_shape.ur.y);
new_shape_1 = new IntBox(is.ur.x, room_shape.ll.y, room_shape.ur.x, room_shape.ur.y);
}
else if (is.ll.y > room_shape.ll.y && is.ll.y == p_obstacle_shape.ll.y && is.ll.y < room_shape.ur.y)
{
new_shape_1 = new IntBox(room_shape.ll.x, room_shape.ll.y, room_shape.ur.x, is.ll.y);
new_shape_2 = new IntBox(room_shape.ll.x, is.ll.y, room_shape.ur.x, room_shape.ur.y);
}
else if (is.ur.y > room_shape.ll.y && is.ur.y == p_obstacle_shape.ur.y && is.ur.y < room_shape.ur.y)
{
new_shape_2 = new IntBox(room_shape.ll.x, room_shape.ll.y, room_shape.ur.x, is.ur.y);
new_shape_1 = new IntBox(room_shape.ll.x, is.ur.y, room_shape.ur.x, room_shape.ur.y);
}
if (new_shape_1 != null)
{
IntBox new_shape_to_be_contained = shape_to_be_contained.intersection(new_shape_1);
if (new_shape_to_be_contained.dimension() > 0)
{
result.add(new IncompleteFreeSpaceExpansionRoom(new_shape_1,
p_incomplete_room.get_layer(), new_shape_to_be_contained));
IncompleteFreeSpaceExpansionRoom new_incomplete_room =
new IncompleteFreeSpaceExpansionRoom( new_shape_2, p_incomplete_room.get_layer(),
shape_to_be_contained.intersection(new_shape_2));
result.addAll(restrain_shape(new_incomplete_room, p_obstacle_shape));
}
}
return result;
}
TileShape[] calculate_tree_shapes(DrillItem p_drill_item)
{
if (this.board == null)
{
return new TileShape[0];
}
TileShape[] result = new TileShape [p_drill_item.tile_shape_count()];
for (int i = 0; i < result.length; ++i)
{
Shape curr_shape = p_drill_item.get_shape(i);
if (curr_shape == null)
{
result[i] = null;
}
else
{
IntBox curr_tile_shape = curr_shape.bounding_box();
int offset_width = this.clearance_compensation_value(p_drill_item.clearance_class_no(), p_drill_item.shape_layer(i));
if (curr_tile_shape == null)
{
System.out.println("BoxShapeSearchTree.calculate_tree_shapes: shape is null");
}
else
{
curr_tile_shape = curr_tile_shape.offset(offset_width);
}
result [i] = curr_tile_shape;
}
}
return result;
}
TileShape[] calculate_tree_shapes(ObstacleArea p_obstacle_area)
{
TileShape[] result = super.calculate_tree_shapes(p_obstacle_area);
for (int i = 0; i < result.length; ++i)
{
result[i] = result[i].bounding_box();
}
return result;
}
TileShape[] calculate_tree_shapes(BoardOutline p_outline)
{
TileShape[] result = super.calculate_tree_shapes(p_outline);
for (int i = 0; i < result.length; ++i)
{
result[i] = result[i].bounding_box();
}
return result;
}
/**
* Used for creating the shapes of a polyline_trace for this tree.
*/
TileShape offset_shape(Polyline p_polyline, int p_half_width, int p_no)
{
return p_polyline.offset_box(p_half_width, p_no);
}
/**
* Used for creating the shapes of a polyline_trace for this tree.
*/
public TileShape[] offset_shapes(Polyline p_polyline, int p_half_width,
int p_from_no, int p_to_no)
{
int from_no = Math.max(p_from_no, 0);
int to_no = Math.min(p_to_no, p_polyline.arr.length -1);
int shape_count = Math.max(to_no - from_no -1, 0);
TileShape[] shape_arr = new TileShape[shape_count];
for (int j = from_no; j < to_no - 1; ++j)
{
shape_arr [j - from_no] = p_polyline.offset_box(p_half_width, j);
}
return shape_arr;
}
}