freerouting/src/main/java/eu/mihosoft/freerouting/autoroute/LocateFoundConnectionAlgo.java

564 lines
22 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.
*
* LocateFoundConnectionAlgo.java
*
* Created on 31. Januar 2006, 08:20
*
*/
package eu.mihosoft.freerouting.autoroute;
import java.util.Collection;
import java.util.SortedSet;
import java.util.LinkedList;
import java.util.Iterator;
import eu.mihosoft.freerouting.geometry.planar.IntPoint;
import eu.mihosoft.freerouting.geometry.planar.FloatPoint;
import eu.mihosoft.freerouting.geometry.planar.TileShape;
import eu.mihosoft.freerouting.board.Connectable;
import eu.mihosoft.freerouting.board.Item;
import eu.mihosoft.freerouting.board.AngleRestriction;
import eu.mihosoft.freerouting.board.ShapeSearchTree;
import eu.mihosoft.freerouting.board.TestLevel;
/**
*
* @author Alfons Wirtz
*/
public abstract class LocateFoundConnectionAlgo
{
/**
* Returns a new Instance of LocateFoundConnectionAlgo or null,
* if p_destination_door is null.
*/
public static LocateFoundConnectionAlgo get_instance(MazeSearchAlgo.Result p_maze_search_result, AutorouteControl p_ctrl,
ShapeSearchTree p_search_tree, AngleRestriction p_angle_restriction, SortedSet<Item> p_ripped_item_list, TestLevel p_test_level)
{
if (p_maze_search_result == null)
{
return null;
}
LocateFoundConnectionAlgo result;
if (p_angle_restriction == AngleRestriction.NINETY_DEGREE || p_angle_restriction == AngleRestriction.FORTYFIVE_DEGREE)
{
result = new LocateFoundConnectionAlgo45Degree(p_maze_search_result, p_ctrl, p_search_tree, p_angle_restriction,
p_ripped_item_list, p_test_level);
}
else
{
result = new LocateFoundConnectionAlgoAnyAngle(p_maze_search_result, p_ctrl, p_search_tree, p_angle_restriction,
p_ripped_item_list, p_test_level);
}
return result;
}
/** Creates a new instance of LocateFoundConnectionAlgo */
protected LocateFoundConnectionAlgo(MazeSearchAlgo.Result p_maze_search_result, AutorouteControl p_ctrl,
ShapeSearchTree p_search_tree, AngleRestriction p_angle_restriction, SortedSet<Item> p_ripped_item_list, TestLevel p_test_level)
{
this.ctrl = p_ctrl;
this.angle_restriction = p_angle_restriction;
this.test_level = p_test_level;
Collection<BacktrackElement> backtrack_list = backtrack(p_maze_search_result, p_ripped_item_list);
this.backtrack_array = new BacktrackElement[backtrack_list.size()];
Iterator<BacktrackElement> it = backtrack_list.iterator();
for (int i = 0; i < backtrack_array.length; ++i)
{
this.backtrack_array[i] = it.next();
}
this.connection_items = new LinkedList<ResultItem>();
BacktrackElement start_info = this.backtrack_array[backtrack_array.length - 1];
if (!(start_info.door instanceof TargetItemExpansionDoor))
{
System.out.println("LocateFoundConnectionAlgo: ItemExpansionDoor expected for start_info.door");
this.start_item = null;
this.start_layer = 0;
this.target_item = null;
this.target_layer = 0;
this.start_door = null;
return;
}
this.start_door = (TargetItemExpansionDoor) start_info.door;
this.start_item = start_door.item;
this.start_layer = start_door.room.get_layer();
this.current_from_door_index = 0;
boolean at_fanout_end = false;
if (p_maze_search_result.destination_door instanceof TargetItemExpansionDoor)
{
TargetItemExpansionDoor curr_destination_door = (TargetItemExpansionDoor) p_maze_search_result.destination_door;
this.target_item = curr_destination_door.item;
this.target_layer = curr_destination_door.room.get_layer();
this.current_from_point = calculate_starting_point(curr_destination_door, p_search_tree);
}
else if (p_maze_search_result.destination_door instanceof ExpansionDrill)
{
// may happen only in case of fanout
this.target_item = null;
ExpansionDrill curr_drill = (ExpansionDrill) p_maze_search_result.destination_door;
this.current_from_point = curr_drill.location.to_float();
this.target_layer = curr_drill.first_layer + p_maze_search_result.section_no_of_door;
at_fanout_end = true;
}
else
{
System.out.println("LocateFoundConnectionAlgo: unexpected type of destination_door");
this.target_item = null;
this.target_layer = 0;
return;
}
this.current_trace_layer = this.target_layer;
this.previous_from_point = this.current_from_point;
boolean connection_done = false;
while (!connection_done)
{
boolean layer_changed = false;
if (at_fanout_end)
{
// do not increase this.current_target_door_index
layer_changed = true;
}
else
{
this.current_target_door_index = this.current_from_door_index + 1;
while (current_target_door_index < this.backtrack_array.length && !layer_changed)
{
if (this.backtrack_array[this.current_target_door_index].door instanceof ExpansionDrill)
{
layer_changed = true;
}
else
{
++this.current_target_door_index;
}
}
}
if (layer_changed)
{
// the next trace leads to a via
ExpansionDrill current_target_drill = (ExpansionDrill) this.backtrack_array[this.current_target_door_index].door;
this.current_target_shape = TileShape.get_instance(current_target_drill.location);
}
else
{
// the next trace leads to the final target
connection_done = true;
this.current_target_door_index = this.backtrack_array.length - 1;
TileShape target_shape = ((Connectable) start_item).get_trace_connection_shape(p_search_tree, start_door.tree_entry_no);
this.current_target_shape = target_shape.intersection(start_door.room.get_shape());
if (this.current_target_shape.dimension() >= 2)
{
// the target is a conduction area, make a save connection
// by shrinking the shape by the trace halfwidth.
double trace_half_width = this.ctrl.compensated_trace_half_width[start_door.room.get_layer()];
TileShape shrinked_shape = (TileShape) this.current_target_shape.offset(-trace_half_width);
if (!shrinked_shape.is_empty())
{
this.current_target_shape = shrinked_shape;
}
}
}
this.current_to_door_index = this.current_from_door_index + 1;
ResultItem next_trace = this.calculate_next_trace(layer_changed, at_fanout_end);
at_fanout_end = false;
this.connection_items.add(next_trace);
}
}
/**
* Calclates the next trace trace of the connection under construction.
* Returns null, if all traces are returned.
*/
private ResultItem calculate_next_trace(boolean p_layer_changed, boolean p_at_fanout_end)
{
Collection<FloatPoint> corner_list = new LinkedList<FloatPoint>();
corner_list.add(this.current_from_point);
if (!p_at_fanout_end)
{
FloatPoint adjusted_start_corner = this.adjust_start_corner();
if (adjusted_start_corner != this.current_from_point)
{
FloatPoint add_corner = calculate_additional_corner(this.current_from_point, adjusted_start_corner,
true, this.angle_restriction);
corner_list.add(add_corner);
corner_list.add(adjusted_start_corner);
this.previous_from_point = this.current_from_point;
this.current_from_point = adjusted_start_corner;
}
}
FloatPoint prev_corner = this.current_from_point;
for (;;)
{
Collection<FloatPoint> next_corners = calculate_next_trace_corners();
if (next_corners.isEmpty())
{
break;
}
Iterator<FloatPoint> it = next_corners.iterator();
while (it.hasNext())
{
FloatPoint curr_next_corner = it.next();
if (curr_next_corner != prev_corner)
{
corner_list.add(curr_next_corner);
this.previous_from_point = this.current_from_point;
this.current_from_point = curr_next_corner;
prev_corner = curr_next_corner;
}
}
}
int next_layer = this.current_trace_layer;
if (p_layer_changed)
{
this.current_from_door_index = this.current_target_door_index + 1;
CompleteExpansionRoom next_room = this.backtrack_array[this.current_from_door_index].next_room;
if (next_room != null)
{
next_layer = next_room.get_layer();
}
}
// Round the new trace corners to Integer.
Collection<IntPoint> rounded_corner_list = new LinkedList<IntPoint>();
Iterator<FloatPoint> it = corner_list.iterator();
IntPoint prev_point = null;
while (it.hasNext())
{
IntPoint curr_point = (it.next()).round();
if (!curr_point.equals(prev_point))
{
rounded_corner_list.add(curr_point);
prev_point = curr_point;
}
}
// Construct the result item
IntPoint[] corner_arr = new IntPoint[rounded_corner_list.size()];
Iterator<IntPoint> it2 = rounded_corner_list.iterator();
for (int i = 0; i < corner_arr.length; ++i)
{
corner_arr[i] = it2.next();
}
ResultItem result = new ResultItem(corner_arr, this.current_trace_layer);
this.current_trace_layer = next_layer;
return result;
}
/**
* Returns the next list of corners for the construction of the trace
* in calculate_next_trace. If the result is emppty, the trace is already completed.
*/
protected abstract Collection<FloatPoint> calculate_next_trace_corners();
/** Test display of the baktrack rooms. */
public void draw(java.awt.Graphics p_graphics, eu.mihosoft.freerouting.boardgraphics.GraphicsContext p_graphics_context)
{
for (int i = 0; i < backtrack_array.length; ++i)
{
CompleteExpansionRoom next_room = backtrack_array[i].next_room;
if (next_room != null)
{
next_room.draw(p_graphics, p_graphics_context, 0.2);
}
ExpandableObject next_door = backtrack_array[i].door;
if (next_door instanceof ExpansionDrill)
{
((ExpansionDrill) next_door).draw(p_graphics, p_graphics_context, 0.2);
}
}
}
/**
* Calculates the starting point of the next trace on p_from_door.item.
* The implementation is not yet optimal for starting points on traces
* or areas.
*/
private static FloatPoint calculate_starting_point(TargetItemExpansionDoor p_from_door, ShapeSearchTree p_search_tree)
{
TileShape connection_shape =
((Connectable) p_from_door.item).get_trace_connection_shape(p_search_tree, p_from_door.tree_entry_no);
connection_shape = connection_shape.intersection(p_from_door.room.get_shape());
return connection_shape.centre_of_gravity().round().to_float();
}
/**
* Creates a list of doors by backtracking from p_destination_door to
* the start door.
* Returns null, if p_destination_door is null.
*/
private static Collection<BacktrackElement> backtrack(MazeSearchAlgo.Result p_maze_search_result, SortedSet<Item> p_ripped_item_list)
{
if (p_maze_search_result == null)
{
return null;
}
Collection<BacktrackElement> result = new LinkedList<BacktrackElement>();
CompleteExpansionRoom curr_next_room = null;
ExpandableObject curr_backtrack_door = p_maze_search_result.destination_door;
MazeSearchElement curr_maze_search_element = curr_backtrack_door.get_maze_search_element(p_maze_search_result.section_no_of_door);
if (curr_backtrack_door instanceof TargetItemExpansionDoor)
{
curr_next_room = ((TargetItemExpansionDoor) curr_backtrack_door).room;
}
else if (curr_backtrack_door instanceof ExpansionDrill)
{
ExpansionDrill curr_drill = (ExpansionDrill) curr_backtrack_door;
curr_next_room = curr_drill.room_arr[curr_drill.first_layer + p_maze_search_result.section_no_of_door];
if (curr_maze_search_element.room_ripped)
{
for (CompleteExpansionRoom tmp_room : curr_drill.room_arr)
{
if (tmp_room instanceof ObstacleExpansionRoom)
{
p_ripped_item_list.add(((ObstacleExpansionRoom) tmp_room).get_item());
}
}
}
}
BacktrackElement curr_backtrack_element = new BacktrackElement(curr_backtrack_door, p_maze_search_result.section_no_of_door, curr_next_room);
for (;;)
{
result.add(curr_backtrack_element);
curr_backtrack_door = curr_maze_search_element.backtrack_door;
if (curr_backtrack_door == null)
{
break;
}
int curr_section_no = curr_maze_search_element.section_no_of_backtrack_door;
if (curr_section_no >= curr_backtrack_door.maze_search_element_count())
{
System.out.println("LocateFoundConnectionAlgo: curr_section_no to big");
curr_section_no = curr_backtrack_door.maze_search_element_count() - 1;
}
if (curr_backtrack_door instanceof ExpansionDrill)
{
ExpansionDrill curr_drill = (ExpansionDrill) curr_backtrack_door;
curr_next_room = curr_drill.room_arr[curr_section_no];
}
else
{
curr_next_room = curr_backtrack_door.other_room(curr_next_room);
}
curr_maze_search_element = curr_backtrack_door.get_maze_search_element(curr_section_no);
curr_backtrack_element = new BacktrackElement(curr_backtrack_door, curr_section_no, curr_next_room);
if (curr_maze_search_element.room_ripped)
{
if (curr_next_room instanceof ObstacleExpansionRoom)
{
p_ripped_item_list.add(((ObstacleExpansionRoom) curr_next_room).get_item());
}
}
}
return result;
}
/**
* Adjusts the start corner, so that a trace starting at this corner is completely
* contained in the start room.
*/
private FloatPoint adjust_start_corner()
{
if (this.current_from_door_index < 0)
{
return this.current_from_point;
}
BacktrackElement curr_from_info = this.backtrack_array[this.current_from_door_index];
if (curr_from_info.next_room == null)
{
return this.current_from_point;
}
double trace_half_width = this.ctrl.compensated_trace_half_width[this.current_trace_layer];
TileShape shrinked_room_shape = (TileShape) curr_from_info.next_room.get_shape().offset(-trace_half_width);
if (shrinked_room_shape.is_empty() || shrinked_room_shape.contains(this.current_from_point))
{
return this.current_from_point;
}
return shrinked_room_shape.nearest_point_approx(this.current_from_point).round().to_float();
}
private static FloatPoint ninety_degree_corner(FloatPoint p_from_point, FloatPoint p_to_point,
boolean p_horizontal_first)
{
double x;
double y;
if (p_horizontal_first)
{
x = p_to_point.x;
y = p_from_point.y;
}
else
{
x = p_from_point.x;
y = p_to_point.y;
}
return new FloatPoint(x, y);
}
private static FloatPoint fortyfive_degree_corner(FloatPoint p_from_point, FloatPoint p_to_point,
boolean p_horizontal_first)
{
double abs_dx = Math.abs(p_to_point.x - p_from_point.x);
double abs_dy = Math.abs(p_to_point.y - p_from_point.y);
double x;
double y;
if (abs_dx <= abs_dy)
{
if (p_horizontal_first)
{
x = p_to_point.x;
if (p_to_point.y >= p_from_point.y)
{
y = p_from_point.y + abs_dx;
}
else
{
y = p_from_point.y - abs_dx;
}
}
else
{
x = p_from_point.x;
if (p_to_point.y > p_from_point.y)
{
y = p_to_point.y - abs_dx;
}
else
{
y = p_to_point.y + abs_dx;
}
}
}
else
{
if (p_horizontal_first)
{
y = p_from_point.y;
if (p_to_point.x > p_from_point.x)
{
x = p_to_point.x - abs_dy;
}
else
{
x = p_to_point.x + abs_dy;
}
}
else
{
y = p_to_point.y;
if (p_to_point.x > p_from_point.x)
{
x = p_from_point.x + abs_dy;
}
else
{
x = p_from_point.x - abs_dy;
}
}
}
return new FloatPoint(x, y);
}
/**
* Calculates an additional corner, so that for the lines from p_from_point to the result corner
* and from the result corner to p_to_point p_angle_restriction is fulfilled.
*/
static FloatPoint calculate_additional_corner(FloatPoint p_from_point, FloatPoint p_to_point,
boolean p_horizontal_first, AngleRestriction p_angle_restriction)
{
FloatPoint result;
if (p_angle_restriction == AngleRestriction.NINETY_DEGREE)
{
result = ninety_degree_corner(p_from_point, p_to_point, p_horizontal_first);
}
else if (p_angle_restriction == AngleRestriction.FORTYFIVE_DEGREE)
{
result = fortyfive_degree_corner(p_from_point, p_to_point, p_horizontal_first);
}
else
{
result = p_to_point;
}
return result;
}
/** The new items implementing the found connection */
public final Collection<ResultItem> connection_items;
/** The start item of the new routed connection */
public final Item start_item;
/** The layer of the connection to the start item */
public final int start_layer;
/** The destination item of the new routed connection */
public final Item target_item;
/** The layer of the connection to the target item */
public final int target_layer;
/**
* The array of backtrack doors from the destination to the start of a found
* connection of the maze search algorithm.
*/
protected final BacktrackElement[] backtrack_array;
protected final AutorouteControl ctrl;
protected final AngleRestriction angle_restriction;
protected final TestLevel test_level;
protected final TargetItemExpansionDoor start_door;
protected FloatPoint current_from_point;
protected FloatPoint previous_from_point;
protected int current_trace_layer;
protected int current_from_door_index;
protected int current_to_door_index;
protected int current_target_door_index;
protected TileShape current_target_shape;
/**
* Type of a single item in the result list connection_items.
* Used to create a new PolylineTrace.
*/
protected static class ResultItem
{
public ResultItem(IntPoint[] p_corners, int p_layer)
{
corners = p_corners;
layer = p_layer;
}
public final IntPoint[] corners;
public final int layer;
}
/**
* Type of the elements of the list returned by this.backtrack().
* Next_room is the common room of the current door and the next
* door in the backtrack list.
*/
protected static class BacktrackElement
{
private BacktrackElement(ExpandableObject p_door, int p_section_no_of_door, CompleteExpansionRoom p_room)
{
door = p_door;
section_no_of_door = p_section_no_of_door;
next_room = p_room;
}
public final ExpandableObject door;
public final int section_no_of_door;
public final CompleteExpansionRoom next_room;
}
}