freerouting/src/main/java/eu/mihosoft/freerouting/board/ShapeSearchTree.java

1209 lines
50 KiB
Java

/*
* 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.
*
* ShapeSearchTree.java
*
* Created on 1. September 2004, 10:13
*/
package eu.mihosoft.freerouting.board;
import eu.mihosoft.freerouting.geometry.planar.ConvexShape;
import eu.mihosoft.freerouting.geometry.planar.ShapeBoundingDirections;
import eu.mihosoft.freerouting.geometry.planar.IntOctagon;
import eu.mihosoft.freerouting.geometry.planar.Line;
import eu.mihosoft.freerouting.geometry.planar.LineSegment;
import eu.mihosoft.freerouting.geometry.planar.Polyline;
import eu.mihosoft.freerouting.geometry.planar.PolylineShape;
import eu.mihosoft.freerouting.geometry.planar.RegularTileShape;
import eu.mihosoft.freerouting.geometry.planar.Shape;
import eu.mihosoft.freerouting.geometry.planar.Side;
import eu.mihosoft.freerouting.geometry.planar.Simplex;
import eu.mihosoft.freerouting.geometry.planar.TileShape;
import eu.mihosoft.freerouting.geometry.planar.IntBox;
import eu.mihosoft.freerouting.geometry.planar.FloatPoint;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeSet;
import eu.mihosoft.freerouting.rules.ClearanceMatrix;
import eu.mihosoft.freerouting.datastructures.Signum;
import eu.mihosoft.freerouting.autoroute.IncompleteFreeSpaceExpansionRoom;
import eu.mihosoft.freerouting.autoroute.CompleteFreeSpaceExpansionRoom;
/**
*
* Elementary geometric search functions making direct use
* of the MinAreaTree in the package eu.mihosoft.freerouting.datastructures.
*
*
* @author Alfons Wirtz
*/
public class ShapeSearchTree extends eu.mihosoft.freerouting.datastructures.MinAreaTree
{
/**
* Creates a new ShapeSearchTree.
* p_compensated_clearance_class_no is the clearance class number for which the shapes of this tree is compensated.
* If p_compensated_clearance_class_no = 0, the shapes are not compensated.
*/
ShapeSearchTree(ShapeBoundingDirections p_directions, BasicBoard p_board, int p_compensated_clearance_class_no)
{
super(p_directions);
this.compensated_clearance_class_no = p_compensated_clearance_class_no;
board = p_board;
}
/**
* Returns, if for the shapes stored in this tree
* clearance compensatiion is used.
*/
public boolean is_clearance_compensation_used()
{
return this.compensated_clearance_class_no > 0;
}
/**
* Return the clearance compensation value of p_clearance_class_no to the clearance compensation class
* of this search tree with on layer p_layer.
* Returns 0, if no clearance compensation is used for this tree.
*/
public int clearance_compensation_value(int p_clearance_class_no, int p_layer)
{
if (p_clearance_class_no <= 0)
{
return 0;
}
int result = board.rules.clearance_matrix.value(p_clearance_class_no, this.compensated_clearance_class_no, p_layer) - board.rules.clearance_matrix.clearance_compensation_value(this.compensated_clearance_class_no, p_layer);
return Math.max(result, 0);
}
/**
* Changes the tree entries from p_keep_at_start_count + 1
* to new_shape_count - 1 - keep_at_end_count to p_changed_entries.
* Special implementation for change_trace for performance reasons
*/
void change_entries(PolylineTrace p_obj, Polyline p_new_polyline,
int p_keep_at_start_count, int p_keep_at_end_count)
{
// calculate the shapes of p_new_polyline from keep_at_start_count to
// new_shape_count - keep_at_end_count - 1;
int compensated_half_width = p_obj.get_half_width() +
this.clearance_compensation_value(p_obj.clearance_class_no(), p_obj.get_layer());
TileShape[] changed_shapes =
this.offset_shapes(p_new_polyline, compensated_half_width, p_keep_at_start_count,
p_new_polyline.arr.length - 1 - p_keep_at_end_count);
int old_shape_count = p_obj.tree_shape_count(this);
int new_shape_count = changed_shapes.length + p_keep_at_start_count + p_keep_at_end_count;
Leaf[] new_leaf_arr = new Leaf[new_shape_count];
TileShape[] new_precalculated_tree_shapes = new TileShape[new_shape_count];
Leaf[] old_entries = p_obj.get_search_tree_entries(this);
for (int i = 0; i < p_keep_at_start_count; ++i)
{
new_leaf_arr[i] = old_entries[i];
new_precalculated_tree_shapes[i] = p_obj.get_tree_shape(this, i);
}
for (int i = p_keep_at_start_count; i < old_shape_count - p_keep_at_end_count; ++i)
{
remove_leaf(old_entries[i]);
}
for (int i = 0; i < p_keep_at_end_count; ++i)
{
int new_index = new_shape_count - p_keep_at_end_count + i;
int old_index = old_shape_count - p_keep_at_end_count + i;
new_leaf_arr[new_index] = old_entries[old_index];
new_leaf_arr[new_index].shape_index_in_object = new_index;
new_precalculated_tree_shapes[new_index] = p_obj.get_tree_shape(this, old_index);
}
// correct the precalculated tree shapes first, because it is used in this.insert
for (int i = p_keep_at_start_count; i < new_shape_count - p_keep_at_end_count; ++i)
{
new_precalculated_tree_shapes[i] = changed_shapes[i - p_keep_at_start_count];
}
p_obj.set_precalculated_tree_shapes(new_precalculated_tree_shapes, this);
for (int i = p_keep_at_start_count; i < new_shape_count - p_keep_at_end_count; ++i)
{
new_leaf_arr[i] = insert(p_obj, i);
}
p_obj.set_search_tree_entries(new_leaf_arr, this);
}
/**
* Merges the tree entries from p_from_trace in front of p_to_trace.
* Special implementation for combine trace for performance reasons.
*/
void merge_entries_in_front(PolylineTrace p_from_trace, PolylineTrace p_to_trace,
Polyline p_joined_polyline, int p_from_entry_no, int p_to_entry_no)
{
int compensated_half_width = p_to_trace.get_half_width() +
this.clearance_compensation_value(p_to_trace.clearance_class_no(), p_to_trace.get_layer());
TileShape[] link_shapes =
this.offset_shapes(p_joined_polyline, compensated_half_width, p_from_entry_no, p_to_entry_no);
boolean change_order =
p_from_trace.first_corner().equals(p_to_trace.first_corner());
// remove the last or first tree entry from p_from_trace and the
// first tree entry from p_to_trace, because they will be replaced by
// the new link entries.
int from_shape_count_minus_1 = p_from_trace.tile_shape_count() - 1;
int remove_no;
if (change_order)
{
remove_no = 0;
}
else
{
remove_no = from_shape_count_minus_1;
}
Leaf[] from_trace_entries = p_from_trace.get_search_tree_entries(this);
Leaf[] to_trace_entries = p_to_trace.get_search_tree_entries(this);
remove_leaf(from_trace_entries[remove_no]);
remove_leaf(to_trace_entries[0]);
int new_shape_count = from_trace_entries.length + link_shapes.length + to_trace_entries.length - 2;
Leaf[] new_leaf_arr = new Leaf[new_shape_count];
int old_to_shape_count = to_trace_entries.length;
TileShape[] new_precalculated_tree_shapes = new TileShape[new_shape_count];
// transfer the tree entries except the last or first from p_from_trace to p_to_trace
for (int i = 0; i < from_shape_count_minus_1; ++i)
{
int from_no;
if (change_order)
{
from_no = from_shape_count_minus_1 - i;
}
else
{
from_no = i;
}
new_precalculated_tree_shapes[i] = p_from_trace.get_tree_shape(this, from_no);
new_leaf_arr[i] = from_trace_entries[from_no];
new_leaf_arr[i].object = p_to_trace;
new_leaf_arr[i].shape_index_in_object = i;
}
for (int i = 1; i < old_to_shape_count; ++i)
{
int curr_ind =
from_shape_count_minus_1 + link_shapes.length + i - 1;
new_precalculated_tree_shapes[curr_ind] = p_to_trace.get_tree_shape(this, i);
new_leaf_arr[curr_ind] = to_trace_entries[i];
new_leaf_arr[curr_ind].shape_index_in_object = curr_ind;
}
// correct the precalculated tree shapes first, because it is used in this.insert
for (int i = 0; i < link_shapes.length; ++i)
{
int curr_ind = from_shape_count_minus_1 + i;
new_precalculated_tree_shapes[curr_ind] = link_shapes[i];
}
p_to_trace.set_precalculated_tree_shapes(new_precalculated_tree_shapes, this);
// create the new link entries
for (int i = 0; i < link_shapes.length; ++i)
{
int curr_ind = from_shape_count_minus_1 + i;
new_leaf_arr[curr_ind] = insert(p_to_trace, curr_ind);
}
p_to_trace.set_search_tree_entries(new_leaf_arr, this);
}
/**
* Merges the tree entries from p_from_trace to the end of p_to_trace.
* Special implementation for combine trace for performance reasons.
*/
void merge_entries_at_end(PolylineTrace p_from_trace, PolylineTrace p_to_trace,
Polyline p_joined_polyline, int p_from_entry_no, int p_to_entry_no)
{
int compensated_half_width = p_to_trace.get_half_width() +
this.clearance_compensation_value(p_to_trace.clearance_class_no(), p_to_trace.get_layer());
TileShape[] link_shapes =
this.offset_shapes(p_joined_polyline, compensated_half_width, p_from_entry_no, p_to_entry_no);
boolean change_order =
p_from_trace.last_corner().equals(p_to_trace.last_corner());
Leaf[] from_trace_entries = p_from_trace.get_search_tree_entries(this);
Leaf[] to_trace_entries = p_to_trace.get_search_tree_entries(this);
// remove the last or first tree entry from p_from_trace and the
// last tree entry from p_to_trace, because they will be replaced by
// the new link entries.
int to_shape_count_minus_1 = p_to_trace.tile_shape_count() - 1;
remove_leaf(to_trace_entries[to_shape_count_minus_1]);
int remove_no;
if (change_order)
{
remove_no = p_from_trace.tile_shape_count() - 1;
}
else
{
remove_no = 0;
}
remove_leaf(from_trace_entries[remove_no]);
int new_shape_count =
from_trace_entries.length + link_shapes.length + to_trace_entries.length - 2;
Leaf[] new_leaf_arr = new Leaf[new_shape_count];
TileShape[] new_precalculated_tree_shapes = new TileShape[new_shape_count];
// transfer the tree entries except the last from the old shapes
// of p_to_trace to the new shapes of p_to_trace
for (int i = 0; i < to_shape_count_minus_1; ++i)
{
new_precalculated_tree_shapes[i] = p_to_trace.get_tree_shape(this, i);
new_leaf_arr[i] = to_trace_entries[i];
}
for (int i = 1; i < from_trace_entries.length; ++i)
{
int curr_ind = to_shape_count_minus_1 + link_shapes.length + i - 1;
int from_no;
if (change_order)
{
from_no = from_trace_entries.length - i - 1;
}
else
{
from_no = i;
}
new_precalculated_tree_shapes[curr_ind] = p_from_trace.get_tree_shape(this, from_no);
new_leaf_arr[curr_ind] = from_trace_entries[from_no];
new_leaf_arr[curr_ind].object = p_to_trace;
new_leaf_arr[curr_ind].shape_index_in_object = curr_ind;
}
// correct the precalculated tree shapes first, because it is used in this.insert
for (int i = 0; i < link_shapes.length; ++i)
{
int curr_ind = to_shape_count_minus_1 + i;
new_precalculated_tree_shapes[curr_ind] = link_shapes[i];
}
p_to_trace.set_precalculated_tree_shapes(new_precalculated_tree_shapes, this);
// create the new link entries
for (int i = 0; i < link_shapes.length; ++i)
{
int curr_ind = to_shape_count_minus_1 + i;
new_leaf_arr[curr_ind] = insert(p_to_trace, curr_ind);
}
p_to_trace.set_search_tree_entries(new_leaf_arr, this);
}
/**
* Trannsfers tree entries from p_from_trace to p_start and p_end_piece
* after a moddle piece was cut out.
* Special implementation for ShapeTraceEntries.fast_cutout_trace for performance reasoms.
*/
void reuse_entries_after_cutout(PolylineTrace p_from_trace, PolylineTrace p_start_piece, PolylineTrace p_end_piece)
{
Leaf[] start_piece_leaf_arr = new Leaf[p_start_piece.polyline().arr.length - 2];
Leaf[] from_trace_entries = p_from_trace.get_search_tree_entries(this);
// transfer the entries at the start of p_from_trace to p_start_piece.
for (int i = 0; i < start_piece_leaf_arr.length - 1; ++i)
{
start_piece_leaf_arr[i] = from_trace_entries[i];
start_piece_leaf_arr[i].object = p_start_piece;
start_piece_leaf_arr[i].shape_index_in_object = i;
from_trace_entries[i] = null;
}
start_piece_leaf_arr[start_piece_leaf_arr.length - 1] = insert(p_start_piece, start_piece_leaf_arr.length - 1);
// create the last tree entry of the start piece.
Leaf[] end_piece_leaf_arr = new Leaf[p_end_piece.polyline().arr.length - 2];
// create the first tree entry of the end piece.
end_piece_leaf_arr[0] = insert(p_end_piece, 0);
for (int i = 1; i < end_piece_leaf_arr.length; ++i)
{
int from_index = from_trace_entries.length - end_piece_leaf_arr.length + i;
end_piece_leaf_arr[i] = from_trace_entries[from_index];
end_piece_leaf_arr[i].object = p_end_piece;
end_piece_leaf_arr[i].shape_index_in_object = i;
from_trace_entries[from_index] = null;
}
p_start_piece.set_search_tree_entries(start_piece_leaf_arr, this);
p_end_piece.set_search_tree_entries(end_piece_leaf_arr, this);
}
/**
* Puts all items in the tree overlapping with p_shape
* on layer p_layer into p_obstacles.
* If p_layer < 0, the layer is ignored.
*/
public void overlapping_objects(ConvexShape p_shape, int p_layer, int[] p_ignore_net_nos,
Set<SearchTreeObject> p_obstacles)
{
Collection<TreeEntry> tree_entries = new LinkedList<TreeEntry>();
overlapping_tree_entries(p_shape, p_layer, p_ignore_net_nos, tree_entries);
if (p_obstacles != null)
{
Iterator<TreeEntry> it = tree_entries.iterator();
while (it.hasNext())
{
TreeEntry curr_entry = it.next();
p_obstacles.add((SearchTreeObject) curr_entry.object);
}
}
}
/**
* Returns all SearchTreeObjects on layer p_layer, which overlap with p_shape.
* If p_layer < 0, the layer is ignored
*/
public Set<SearchTreeObject> overlapping_objects(ConvexShape p_shape, int p_layer)
{
Set<SearchTreeObject> result = new TreeSet<SearchTreeObject>();
this.overlapping_objects(p_shape, p_layer, new int[0], result);
return result;
}
/**
* Puts all tree entries overlapping with p_shape
* on layer p_layer into the list p_obstacles.
* If p_layer < 0, the layer is ignored.
*/
public void overlapping_tree_entries(ConvexShape p_shape, int p_layer, Collection<TreeEntry> p_tree_entries)
{
overlapping_tree_entries(p_shape, p_layer, new int[0], p_tree_entries);
}
/**
* Puts all tree entries overlapping with p_shape
* on layer p_layer into the list p_obstacles.
* If p_layer < 0, the layer is ignored.
* tree_entries with object containing a net number of p_ignore_net_nos are ignored.
*/
public void overlapping_tree_entries(ConvexShape p_shape, int p_layer, int[] p_ignore_net_nos,
Collection<TreeEntry> p_tree_entries)
{
if (p_shape == null)
{
return;
}
if (p_tree_entries == null)
{
System.out.println("eu.mihosoft.freerouting.board.ShapeSearchTree.overlaps: p_obstacle_entries is null");
return;
}
RegularTileShape bounds = p_shape.bounding_shape(bounding_directions);
if (bounds == null)
{
System.out.println("eu.mihosoft.freerouting.board.ShapeSearchTree.overlaps: p_shape not bounded");
return;
}
Collection<Leaf> tmp_list = this.overlaps(bounds);
Iterator<Leaf> it = tmp_list.iterator();
boolean is_45_degree = p_shape instanceof IntOctagon;
while (it.hasNext())
{
Leaf curr_leaf = it.next();
SearchTreeObject curr_object = (SearchTreeObject) curr_leaf.object;
int shape_index = curr_leaf.shape_index_in_object;
boolean ignore_object =
p_layer >= 0 && curr_object.shape_layer(shape_index) != p_layer;
if (!ignore_object)
{
for (int i = 0; i < p_ignore_net_nos.length; ++i)
{
if (!curr_object.is_obstacle(p_ignore_net_nos[i]))
{
ignore_object = true;
}
}
}
if (!ignore_object)
{
TileShape curr_shape =
curr_object.get_tree_shape(this, curr_leaf.shape_index_in_object);
boolean add_item;
if (is_45_degree && curr_shape instanceof IntOctagon)
// in this case the check for intersection is redundant and
// therefore skipped for performance reasons
{
add_item = true;
}
else
{
add_item = curr_shape.intersects(p_shape);
}
if (add_item)
{
TreeEntry new_entry =
new TreeEntry(curr_object, shape_index);
p_tree_entries.add(new_entry);
}
}
}
}
/**
* Looks up all entries in the search tree, so that inserting an item with
* shape p_shape, net number p_net_no, clearance type p_cl_type and
* layer p_layer whould produce a clearance violation, and puts them into
* the set p_obstacle_entries.
* The elements in p_obstacle_entries are of type TreeEntry.
* if p_layer < 0, the layer is ignored.
* Used only internally, because the clearance compensation is not taken iinnto account.
*/
void overlapping_tree_entries_with_clearance(ConvexShape p_shape, int p_layer, int[] p_ignore_net_nos,
int p_cl_type, Collection<TreeEntry> p_obstacle_entries)
{
if (p_shape == null)
{
return;
}
if (p_obstacle_entries == null)
{
System.out.println("eu.mihosoft.freerouting.board.ShapeSearchTree.overlaps_with_clearance: p_obstacle_entries is null");
return;
}
ClearanceMatrix cl_matrix = board.rules.clearance_matrix;
RegularTileShape bounds = p_shape.bounding_shape(bounding_directions);
if (bounds == null)
{
System.out.println("eu.mihosoft.freerouting.board.ShapeSearchTree.overlaps_with_clearance: p_shape is not bounded");
bounds = board.get_bounding_box();
}
int max_clearance =
(int) (1.2 * cl_matrix.max_value(p_cl_type, p_layer));
// search with the bounds enlarged by the maximum clearance to
// get all candidates for overlap
// a factor less than sqr2 has evtl. be added because
// enlarging is not symmetric.
RegularTileShape offset_bounds =
(RegularTileShape) bounds.offset(max_clearance);
Collection<Leaf> tmp_list = overlaps(offset_bounds);
Iterator<Leaf> it1 = tmp_list.iterator();
// sort the found items by its clearances tp p_cl_type on layer p_layer
Set<EntrySortedByClearance> sorted_items = new TreeSet<EntrySortedByClearance>();
while (it1.hasNext())
{
Leaf curr_leaf = it1.next();
Item curr_item = (Item) curr_leaf.object;
int shape_index = curr_leaf.shape_index_in_object;
boolean ignore_item =
p_layer >= 0 && curr_item.shape_layer(shape_index) != p_layer;
if (!ignore_item)
{
for (int i = 0; i < p_ignore_net_nos.length; ++i)
{
if (!curr_item.is_obstacle(p_ignore_net_nos[i]))
{
ignore_item = true;
}
}
}
if (!ignore_item)
{
int curr_clearance =
cl_matrix.value(p_cl_type, curr_item.clearance_class_no(), p_layer);
EntrySortedByClearance sorted_ob =
new EntrySortedByClearance(curr_leaf, curr_clearance);
sorted_items.add(sorted_ob);
}
}
Iterator<EntrySortedByClearance> it = sorted_items.iterator();
int curr_half_clearance = 0;
ConvexShape curr_offset_shape = p_shape;
while (it.hasNext())
{
EntrySortedByClearance tmp_entry = it.next();
int tmp_half_clearance = tmp_entry.clearance / 2;
if (tmp_half_clearance != curr_half_clearance)
{
curr_half_clearance = tmp_half_clearance;
curr_offset_shape =
(TileShape) p_shape.enlarge(curr_half_clearance);
}
TileShape tmp_shape =
tmp_entry.leaf.object.get_tree_shape(this, tmp_entry.leaf.shape_index_in_object);
// enlarge both item shapes by the half clearance to create
// symmetry.
ConvexShape tmp_offset_shape =
(ConvexShape) tmp_shape.enlarge(curr_half_clearance);
if (curr_offset_shape.intersects(tmp_offset_shape))
{
p_obstacle_entries.add(new TreeEntry(tmp_entry.leaf.object, tmp_entry.leaf.shape_index_in_object));
}
}
}
/**
* Puts all items in the tree overlapping with p_shape
* on layer p_layer into p_obstacles, if p_obstacles != null.
* If p_layer < 0, the layer is ignored.
*/
public void overlapping_objects_with_clearance(ConvexShape p_shape, int p_layer, int[] p_ignore_net_nos,
int p_cl_type, Set<SearchTreeObject> p_obstacles)
{
Collection<TreeEntry> tree_entries = new LinkedList<TreeEntry>();
if (this.is_clearance_compensation_used())
{
overlapping_tree_entries(p_shape, p_layer, p_ignore_net_nos, tree_entries);
}
else
{
overlapping_tree_entries_with_clearance(p_shape, p_layer, p_ignore_net_nos, p_cl_type, tree_entries);
}
if (p_obstacles != null)
{
Iterator<TreeEntry> it = tree_entries.iterator();
while (it.hasNext())
{
TreeEntry curr_entry = it.next();
p_obstacles.add((SearchTreeObject) curr_entry.object);
}
}
}
/**
* Returns items, which overlap with p_shape on layer p_layer
* inclusive clearance.
* p_clearance_class is the index in the clearance matrix,
* which describes the required clearance restrictions to other items.
* The function may also return items, which are nearly overlapping,
* but do not overlap with exact calculation.
* If p_layer < 0, the layer is ignored.
*/
public Set<Item> overlapping_items_with_clearance(ConvexShape p_shape, int p_layer, int[] p_ignore_net_nos,
int p_clearance_class)
{
Set<SearchTreeObject> overlaps = new TreeSet<SearchTreeObject>();
this.overlapping_objects_with_clearance(p_shape, p_layer,
p_ignore_net_nos, p_clearance_class, overlaps);
Set<Item> result = new TreeSet<Item>();
for (SearchTreeObject curr_object : overlaps)
{
if (curr_object instanceof Item)
{
result.add((Item) curr_object);
}
}
return result;
}
/**
* Returns all objects of class TreeEntry, which overlap with p_shape on layer p_layer
* inclusive clearance.
* p_clearance_class is the index in the clearance matrix,
* which describes the required clearance restrictions to other items.
* If p_layer < 0, the layer is ignored.
*/
public Collection<TreeEntry> overlapping_tree_entries_with_clearance(ConvexShape p_shape, int p_layer,
int[] p_ignore_net_nos, int p_clearance_class)
{
Collection<TreeEntry> result = new LinkedList<TreeEntry>();
if (this.is_clearance_compensation_used())
{
this.overlapping_tree_entries(p_shape, p_layer, p_ignore_net_nos, result);
}
else
{
this.overlapping_tree_entries_with_clearance(p_shape, p_layer, p_ignore_net_nos,
p_clearance_class, result);
}
return result;
}
/**
* 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.
* If p_ignore_shape != null, objects of type CompleteFreeSpaceExpansionRoom,
* whose intersection with the shape of p_room is containes in p_ignore_shape,
* are ignored.
*/
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() == null)
{
System.out.println("ShapeSearchTree.complete_shape: p_shape_to_be_contained != null expected");
return new LinkedList<IncompleteFreeSpaceExpansionRoom>();
}
if (this.root == null)
{
return new LinkedList<IncompleteFreeSpaceExpansionRoom>();
}
TileShape start_shape = board.get_bounding_box();
if (p_room.get_shape() != null)
{
start_shape = start_shape.intersection(p_room.get_shape());
}
RegularTileShape bounding_shape = start_shape.bounding_shape(this.bounding_directions);
Collection<IncompleteFreeSpaceExpansionRoom> result = new LinkedList<IncompleteFreeSpaceExpansionRoom>();
if (start_shape.dimension() == 2)
{
IncompleteFreeSpaceExpansionRoom new_room = new IncompleteFreeSpaceExpansionRoom(start_shape,
p_room.get_layer(), p_room.get_contained_shape());
result.add(new_room);
}
this.node_stack.reset();
this.node_stack.push(this.root);
TreeNode curr_node;
int room_layer = p_room.get_layer();
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)
{
TileShape curr_object_shape = curr_object.get_tree_shape(this, shape_index);
Collection<IncompleteFreeSpaceExpansionRoom> new_result = new LinkedList<IncompleteFreeSpaceExpansionRoom>();
RegularTileShape new_bounding_shape = IntOctagon.EMPTY;
for (IncompleteFreeSpaceExpansionRoom curr_incomplete_room : result)
{
boolean something_changed = false;
TileShape intersection = curr_incomplete_room.get_shape().intersection(curr_object_shape);
if (intersection.dimension() == 2)
{
boolean ignore_expansion_room =
curr_object instanceof CompleteFreeSpaceExpansionRoom && p_ignore_shape != null && p_ignore_shape.contains(intersection);
// cannot happen in free angle roouting, because then expansion_rooms
// may not overlap. Therefore that can be removed as soon as special
// function for 45-degree routing is used.
if (!ignore_expansion_room)
{
something_changed = true;
new_result.addAll(restrain_shape(curr_incomplete_room, curr_object_shape));
for (IncompleteFreeSpaceExpansionRoom tmp_room : new_result)
{
new_bounding_shape = new_bounding_shape.union(tmp_room.get_shape().bounding_shape(this.bounding_directions));
}
}
}
if (!something_changed)
{
new_result.add(curr_incomplete_room);
new_bounding_shape = new_bounding_shape.union(curr_incomplete_room.get_shape().bounding_shape(this.bounding_directions));
}
}
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);
}
}
}
result = divide_large_room(result, board.get_bounding_box());
return result;
}
/**
* Restrains the shape of p_incomplete_room to a TileShape, 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. If that is not possible, several rooms are returned with shapes,
* which intersect with p_incomplete_room.get_contained_shape().
*/
private Collection<IncompleteFreeSpaceExpansionRoom> restrain_shape(IncompleteFreeSpaceExpansionRoom p_incomplete_room, TileShape 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 p_points_to_be_con.tained
// Then insersect p_shape with the halfplane defined by the
// opposite of this line.
Simplex obstacle_simplex = p_obstacle_shape.to_Simplex(); // otherwise border_lines of lenth 0 for octagons may not be handeled correctly
TileShape shape_to_be_contained = p_incomplete_room.get_contained_shape().to_Simplex(); // There may be a performance problem, if a point shape is represented as an octagon
Collection<IncompleteFreeSpaceExpansionRoom> result = new LinkedList<IncompleteFreeSpaceExpansionRoom>();
TileShape room_shape = p_incomplete_room.get_shape();
int layer = p_incomplete_room.get_layer();
if (shape_to_be_contained.is_empty())
{
if (this.board.get_test_level().ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal())
{
System.out.println("ShapeSearchTree.restrain_shape: p_shape_to_be_contained is empty");
}
return result;
}
Line cut_line = null;
double cut_line_distance = -1;
for (int i = 0; i < obstacle_simplex.border_line_count(); ++i)
{
LineSegment curr_line_segment = new LineSegment(obstacle_simplex, i);
if (room_shape.is_intersected_interiour_by(curr_line_segment))
{
// otherwise curr_object may not touch the intersection
// of p_shape with the half_plane defined by the cut_line.
// That may lead to problems when creating the ExpansionRooms.
Line curr_line = obstacle_simplex.border_line(i);
double curr_min_distance = shape_to_be_contained.distance_to_the_left(curr_line);
if (curr_min_distance > cut_line_distance)
{
cut_line_distance = curr_min_distance;
cut_line = curr_line.opposite();
}
}
}
if (cut_line != null)
{
TileShape result_piece = TileShape.get_instance(cut_line);
if (room_shape != null)
{
result_piece = room_shape.intersection(result_piece);
}
if (result_piece.dimension() >= 2)
{
result.add(new IncompleteFreeSpaceExpansionRoom(result_piece, layer, shape_to_be_contained));
}
}
else
{
// There is no cut line, so that all p_shape_to_be_contained is
// completely on the right side of that line. Search a cut line, so that
// at least part of p_shape_to_be_contained is on the right side.
if (shape_to_be_contained.dimension() < 1)
{
// There is already a completed expansion room around p_shape_to_be_contained.
return result;
}
for (int i = 0; i < obstacle_simplex.border_line_count(); ++i)
{
LineSegment curr_line_segment = new LineSegment(obstacle_simplex, i);
if (room_shape.is_intersected_interiour_by(curr_line_segment))
{
Line curr_line = obstacle_simplex.border_line(i);
if (shape_to_be_contained.side_of(curr_line) == Side.COLLINEAR)
{
// curr_line intersects with the interiour of p_shape_to_be_contained
cut_line = curr_line.opposite();
break;
}
}
}
if (cut_line == null)
{
// cut line not found, parts or the whole of p_shape may be already
// occupied from somewhere else.
return result;
}
// Calculate the new shape to be contained in the result shape.
TileShape cut_half_plane = TileShape.get_instance(cut_line);
TileShape new_shape_to_be_contained = shape_to_be_contained.intersection(cut_half_plane);
TileShape result_piece;
if (room_shape == null)
{
result_piece = cut_half_plane;
}
else
{
result_piece = room_shape.intersection(cut_half_plane);
}
if (result_piece.dimension() >= 2)
{
result.add(new IncompleteFreeSpaceExpansionRoom(result_piece, layer, new_shape_to_be_contained));
}
TileShape opposite_half_plane = TileShape.get_instance(cut_line.opposite());
TileShape rest_piece;
if (room_shape == null)
{
rest_piece = opposite_half_plane;
}
else
{
rest_piece = room_shape.intersection(opposite_half_plane);
}
if (rest_piece.dimension() >= 2)
{
TileShape rest_shape_to_be_contained = shape_to_be_contained.intersection(opposite_half_plane);
IncompleteFreeSpaceExpansionRoom rest_incomplete_room = new IncompleteFreeSpaceExpansionRoom(rest_piece, layer, rest_shape_to_be_contained);
result.addAll(restrain_shape(rest_incomplete_room, obstacle_simplex));
}
}
return result;
}
/**
* Reduces the first or last shape of p_trace at a tie pin, so that the autorouter algorithm
* can find a connection for a different net.
*/
public void reduce_trace_shape_at_tie_pin(Pin p_tie_pin, PolylineTrace p_trace)
{
TileShape pin_shape = p_tie_pin.get_tree_shape_on_layer(this, p_trace.get_layer());
FloatPoint compare_corner;
int trace_shape_no;
if (p_trace.first_corner().equals(p_tie_pin.get_center()))
{
trace_shape_no = 0;
compare_corner = p_trace.polyline().corner_approx(1);
}
else if (p_trace.last_corner().equals(p_tie_pin.get_center()))
{
trace_shape_no = p_trace.corner_count() - 2;
compare_corner = p_trace.polyline().corner_approx(p_trace.corner_count() - 2);
}
else
{
return;
}
TileShape trace_shape = p_trace.get_tree_shape(this, trace_shape_no);
TileShape intersection = trace_shape.intersection(pin_shape);
if (intersection.dimension() < 2)
{
return;
}
TileShape[] shape_pieces = trace_shape.cutout(pin_shape);
TileShape new_trace_shape = Simplex.EMPTY;
for (int i = 0; i < shape_pieces.length; ++i)
{
if (shape_pieces[i].dimension() == 2)
{
if (new_trace_shape == Simplex.EMPTY || shape_pieces[i].contains(compare_corner))
{
new_trace_shape = shape_pieces[i];
}
}
}
change_item_shape(p_trace, trace_shape_no, new_trace_shape);
}
/**
* Changes the shape with index p_shape_no of this item to p_new_shape
* and updates the entry in the tree.
*/
void change_item_shape(Item p_item, int p_shape_no, TileShape p_new_shape)
{
Leaf[] old_entries = p_item.get_search_tree_entries(this);
Leaf[] new_leaf_arr = new Leaf[old_entries.length];
TileShape[] new_precalculated_tree_shapes = new TileShape[old_entries.length];
remove_leaf(old_entries[p_shape_no]);
for (int i = 0; i < new_precalculated_tree_shapes.length; ++i)
{
if (i == p_shape_no)
{
new_precalculated_tree_shapes[i] = p_new_shape;
}
else
{
new_precalculated_tree_shapes[i] = p_item.get_tree_shape(this, i);
new_leaf_arr[i] = old_entries[i];
}
}
p_item.set_precalculated_tree_shapes(new_precalculated_tree_shapes, this);
new_leaf_arr[p_shape_no] = insert(p_item, p_shape_no);
p_item.set_search_tree_entries(new_leaf_arr, this);
}
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
{
TileShape curr_tile_shape;
if (this.board.rules.get_trace_angle_restriction() == AngleRestriction.NINETY_DEGREE)
{
curr_tile_shape = curr_shape.bounding_box();
}
else if (this.board.rules.get_trace_angle_restriction() == AngleRestriction.FORTYFIVE_DEGREE)
{
curr_tile_shape = curr_shape.bounding_octagon();
}
else
{
curr_tile_shape = curr_shape.bounding_tile();
}
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("ShapeSearchTree.calculate_tree_shapes: shape is null");
}
else
{
curr_tile_shape = (TileShape) curr_tile_shape.enlarge(offset_width);
}
result[i] = curr_tile_shape;
}
}
return result;
}
TileShape[] calculate_tree_shapes(ObstacleArea p_obstacle_area)
{
if (this.board == null)
{
return new TileShape[0];
}
TileShape[] convex_shapes = p_obstacle_area.split_to_convex();
if (convex_shapes == null)
{
return new TileShape[0];
}
double max_tree_shape_width = 50000;
if (this.board.communication.host_cad_exists())
{
max_tree_shape_width = Math.min(500 * this.board.communication.get_resolution(Unit.MIL), max_tree_shape_width);
// Problem with low resolution on Kicad.
// Called only for designs from host cad systems because otherwise the old sample.dsn gets to many tree shapes.
}
Collection<TileShape> tree_shape_list = new LinkedList<TileShape>();
for (int i = 0; i < convex_shapes.length; ++i)
{
TileShape curr_convex_shape = convex_shapes[i];
int offset_width = this.clearance_compensation_value(p_obstacle_area.clearance_class_no(), p_obstacle_area.get_layer());
curr_convex_shape = (TileShape) curr_convex_shape.enlarge(offset_width);
TileShape[] curr_tree_shapes = curr_convex_shape.divide_into_sections(max_tree_shape_width);
for (int j = 0; j < curr_tree_shapes.length; ++j)
{
tree_shape_list.add(curr_tree_shapes[j]);
}
}
TileShape[] result = new TileShape[tree_shape_list.size()];
Iterator<TileShape> it = tree_shape_list.iterator();
for (int i = 0; i < result.length; ++i)
{
result[i] = it.next();
}
return result;
}
TileShape[] calculate_tree_shapes(BoardOutline p_board_outline)
{
if (this.board == null)
{
return new TileShape[0];
}
TileShape[] result;
if (p_board_outline.keepout_outside_outline_generated())
{
TileShape[] convex_shapes = p_board_outline.get_keepout_area().split_to_convex();
if (convex_shapes == null)
{
return new TileShape[0];
}
Collection<TileShape> tree_shape_list = new LinkedList<TileShape>();
for (int layer_no = 0; layer_no < this.board.layer_structure.arr.length; ++layer_no)
{
for (int i = 0; i < convex_shapes.length; ++i)
{
TileShape curr_convex_shape = convex_shapes[i];
int offset_width = this.clearance_compensation_value(p_board_outline.clearance_class_no(), 0);
curr_convex_shape = (TileShape) curr_convex_shape.enlarge(offset_width);
tree_shape_list.add(curr_convex_shape);
}
}
result = new TileShape[tree_shape_list.size()];
Iterator<TileShape> it = tree_shape_list.iterator();
for (int i = 0; i < result.length; ++i)
{
result[i] = it.next();
}
}
else
{
// Only the line shapes of the outline are inserted as obstales into the tree.
result = new TileShape[p_board_outline.line_count() * this.board.layer_structure.arr.length];
int half_width = p_board_outline.get_half_width();
Line[] curr_line_arr = new Line[3];
int curr_no = 0;
for (int layer_no = 0; layer_no < this.board.layer_structure.arr.length; ++layer_no)
{
for (int shape_no = 0; shape_no < p_board_outline.shape_count(); ++shape_no)
{
PolylineShape curr_outline_shape = p_board_outline.get_shape(shape_no);
int border_line_count = curr_outline_shape.border_line_count();
curr_line_arr[0] = curr_outline_shape.border_line(border_line_count - 1);
for (int i = 0; i < border_line_count; ++i)
{
curr_line_arr[1] = curr_outline_shape.border_line(i);
curr_line_arr[2] = curr_outline_shape.border_line((i + 1) % border_line_count);
Polyline tmp_polyline = new Polyline(curr_line_arr);
int cmp_value = this.clearance_compensation_value(p_board_outline.clearance_class_no(), 0);
result[curr_no] = tmp_polyline.offset_shape(half_width + cmp_value, 0);
++curr_no;
curr_line_arr[0] = curr_line_arr[1];
}
}
}
}
return result;
}
/**
* Used for creating the shapes of a polyline_trace for this tree.
* Overwritten in derived classes.
*/
TileShape offset_shape(Polyline p_polyline, int p_half_width, int p_no)
{
return p_polyline.offset_shape(p_half_width, p_no);
}
/**
* Used for creating the shapes of a polyline_trace for this tree.
* Overwritten in derived classes.
*/
public TileShape[] offset_shapes(Polyline p_polyline, int p_half_width,
int p_from_no, int p_to_no)
{
return p_polyline.offset_shapes(p_half_width, p_from_no, p_to_no);
}
TileShape[] calculate_tree_shapes(PolylineTrace p_trace)
{
if (this.board == null)
{
return new TileShape[0];
}
int offset_width = p_trace.get_half_width() +
this.clearance_compensation_value(p_trace.clearance_class_no(), p_trace.get_layer());
TileShape[] result = new TileShape[p_trace.tile_shape_count()];
for (int i = 0; i < result.length; ++i)
{
result[i] = this.offset_shape(p_trace.polyline(), offset_width, i);
}
return result;
}
/**
* Makes shure that on each layer there will be more than 1 IncompleteFreeSpaceExpansionRoom,
* even if there are no objects on the layer. Otherwise the maze search algprithm gets problems
* with vias.
*/
protected Collection<IncompleteFreeSpaceExpansionRoom> divide_large_room(
Collection<IncompleteFreeSpaceExpansionRoom> p_room_list, IntBox p_board_bounding_box)
{
if (p_room_list.size() != 1)
{
return p_room_list;
}
IncompleteFreeSpaceExpansionRoom curr_room = p_room_list.iterator().next();
IntBox room_bounding_box = curr_room.get_shape().bounding_box();
if (2 * room_bounding_box.height() <= p_board_bounding_box.height() || 2 * room_bounding_box.width() <= p_board_bounding_box.width())
{
return p_room_list;
}
double max_section_width = 0.5 * Math.max(p_board_bounding_box.height(), p_board_bounding_box.width());
TileShape[] section_arr = curr_room.get_shape().divide_into_sections(max_section_width);
Collection<IncompleteFreeSpaceExpansionRoom> result = new LinkedList<IncompleteFreeSpaceExpansionRoom>();
for (TileShape curr_section : section_arr)
{
TileShape curr_shape_to_be_contained = curr_section.intersection(curr_room.get_contained_shape());
IncompleteFreeSpaceExpansionRoom curr_section_room =
new IncompleteFreeSpaceExpansionRoom(curr_section, curr_room.get_layer(),
curr_shape_to_be_contained);
result.add(curr_section_room);
}
return result;
}
boolean validate_entries(Item p_item)
{
Leaf[] curr_tree_entries = p_item.get_search_tree_entries(this);
for (int i = 0; i < curr_tree_entries.length; ++i)
{
Leaf curr_leaf = curr_tree_entries[i];
if (curr_leaf.shape_index_in_object != i)
{
System.out.println("tree entry inconsistent for Item");
return false;
}
}
return true;
}
/**
* The clearance class number for which the shapes of this tree is compensated.
* If compensated_clearance_class_no = 0, the shapes are not compensated.
*/
public final int compensated_clearance_class_no;
protected final BasicBoard board;
/** used in objects of class EntrySortedByClearance */
static private int last_generated_id_no = 0;
/**
* created for sorting Items according to their clearance
* to p_cl_type on layer p_layer
*/
static private class EntrySortedByClearance implements Comparable<EntrySortedByClearance>
{
EntrySortedByClearance(Leaf p_leaf, int p_clearance)
{
leaf = p_leaf;
clearance = p_clearance;
if (last_generated_id_no >= Integer.MAX_VALUE)
{
last_generated_id_no = 0;
}
else
{
++last_generated_id_no;
}
entry_id_no = last_generated_id_no;
}
public int compareTo(EntrySortedByClearance p_other)
{
if (clearance != p_other.clearance)
{
return Signum.as_int(clearance - p_other.clearance);
}
return entry_id_no - p_other.entry_id_no;
}
Leaf leaf;
int clearance;
private final int entry_id_no;
}
}