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

409 lines
18 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.
*
* MazeShoveTraceAlgo.java
*
* Created on 10. Mai 2006, 06:41
*
*/
package eu.mihosoft.freerouting.autoroute;
import java.util.Collection;
import eu.mihosoft.freerouting.geometry.planar.TileShape;
import eu.mihosoft.freerouting.geometry.planar.Line;
import eu.mihosoft.freerouting.geometry.planar.Polyline;
import eu.mihosoft.freerouting.geometry.planar.Point;
import eu.mihosoft.freerouting.geometry.planar.FloatPoint;
import eu.mihosoft.freerouting.geometry.planar.FloatLine;
import eu.mihosoft.freerouting.geometry.planar.Side;
import eu.mihosoft.freerouting.geometry.planar.Direction;
import eu.mihosoft.freerouting.geometry.planar.LineSegment;
import eu.mihosoft.freerouting.board.Item;
import eu.mihosoft.freerouting.board.RoutingBoard;
import eu.mihosoft.freerouting.board.ShoveTraceAlgo;
/**
* Auxiliary functions used in MazeSearchAlgo.
*
* @author Alfons Wirtz
*/
public class MazeShoveTraceAlgo
{
/**
* Returns false, if the algorithm did not succeed and trying to shove from another door section
* may be more successful.
*/
public static boolean check_shove_trace_line(MazeListElement p_list_element,
ObstacleExpansionRoom p_obstacle_room, RoutingBoard p_board, AutorouteControl p_ctrl,
boolean p_shove_to_the_left, Collection<DoorSection> p_to_door_list)
{
if (!(p_list_element.door instanceof ExpansionDoor))
{
return true;
}
ExpansionDoor from_door = (ExpansionDoor) p_list_element.door;
if (!(p_obstacle_room.get_item() instanceof eu.mihosoft.freerouting.board.PolylineTrace))
{
return true;
}
eu.mihosoft.freerouting.board.PolylineTrace obstacle_trace = (eu.mihosoft.freerouting.board.PolylineTrace)p_obstacle_room.get_item();
int trace_layer = p_obstacle_room.get_layer();
// only traces with the same halfwidth and the same clearance class can be shoved.
if (obstacle_trace.get_half_width() != p_ctrl.trace_half_width[trace_layer]
|| obstacle_trace.clearance_class_no() != p_ctrl.trace_clearance_class_no)
{
return true;
}
double compensated_trace_half_width = p_ctrl.compensated_trace_half_width[trace_layer];
TileShape from_door_shape = from_door.get_shape();
if (from_door_shape.max_width() < 2 * compensated_trace_half_width)
{
return true;
}
int trace_corner_no = p_obstacle_room.get_index_in_item();
Polyline trace_polyline = obstacle_trace.polyline();
if (trace_corner_no >= trace_polyline.arr.length - 1)
{
System.out.println("MazeShoveTraceAlgo.check_shove_trace_line: trace_corner_no to big");
return false;
}
Collection<ExpansionDoor> room_doors = p_obstacle_room.get_doors();
// The side of the trace line seen from the doors to expand.
// Used to determine, if a door is on the right side to put it into the p_door_list.
LineSegment shove_line_segment;
if (from_door.dimension == 2)
{
// shove from a link door into the direction of the other link door.
CompleteExpansionRoom other_room = from_door.other_room(p_obstacle_room);
if (!(other_room instanceof ObstacleExpansionRoom))
{
return false;
}
if (!end_points_matching(obstacle_trace, ((ObstacleExpansionRoom)other_room).get_item()))
{
return false;
}
FloatPoint door_center = from_door_shape.centre_of_gravity();
FloatPoint corner_1 = trace_polyline.corner_approx(trace_corner_no);
FloatPoint corner_2 = trace_polyline.corner_approx(trace_corner_no + 1);
if (corner_1.distance_square(corner_2) < 1)
{
// shove_line_segment may be reduced to a point
return false;
}
boolean shove_into_direction_of_trace_start =
door_center.distance_square(corner_2) < door_center.distance_square(corner_1);
shove_line_segment = new LineSegment(trace_polyline, trace_corner_no + 1);
if (shove_into_direction_of_trace_start)
{
// shove from the endpoint to the start point of the line segment
shove_line_segment = shove_line_segment.opposite();
}
}
else
{
CompleteExpansionRoom from_room = from_door.other_room(p_obstacle_room);
FloatPoint from_point = from_room.get_shape().centre_of_gravity();
Line shove_trace_line = trace_polyline.arr[trace_corner_no + 1];
FloatLine door_line_segment = from_door_shape.diagonal_corner_segment();
Side side_of_trace_line = shove_trace_line.side_of(door_line_segment.a, 0);
FloatLine polar_line_segment = from_door_shape.polar_line_segment(from_point);
boolean door_line_swapped =
polar_line_segment.b.distance_square(door_line_segment.a) <
polar_line_segment.a.distance_square(door_line_segment.a);
boolean section_ok;
// shove only from the right most section to the right or from the left most section to the left.
double shape_entry_check_distance = compensated_trace_half_width + 5;
double check_dist_square = shape_entry_check_distance * shape_entry_check_distance;
if (p_shove_to_the_left && !door_line_swapped || !p_shove_to_the_left && door_line_swapped)
{
section_ok =
p_list_element.section_no_of_door == p_list_element.door.maze_search_element_count() - 1
&& (p_list_element.shape_entry.a.distance_square(door_line_segment.b) <= check_dist_square
|| p_list_element.shape_entry.b.distance_square(door_line_segment.b) <= check_dist_square);
}
else
{
section_ok =
p_list_element.section_no_of_door == 0
&& (p_list_element.shape_entry.a.distance_square(door_line_segment.a) <= check_dist_square
|| p_list_element.shape_entry.b.distance_square(door_line_segment.a) <= check_dist_square);
}
if (!section_ok)
{
return false;
}
// create the line segment for shoving
FloatLine shrinked_line_segment = polar_line_segment.shrink_segment(compensated_trace_half_width);
Direction perpendicular_direction = shove_trace_line.direction().turn_45_degree(2);
if (side_of_trace_line == Side.ON_THE_LEFT)
{
if (p_shove_to_the_left)
{
Line start_closing_line = new Line(shrinked_line_segment.b.round(), perpendicular_direction);
shove_line_segment =
new LineSegment(start_closing_line, trace_polyline.arr[trace_corner_no + 1],
trace_polyline.arr[trace_corner_no + 2]);
}
else
{
Line start_closing_line = new Line(shrinked_line_segment.a.round(), perpendicular_direction);
shove_line_segment =
new LineSegment(start_closing_line, trace_polyline.arr[trace_corner_no + 1].opposite(),
trace_polyline.arr[trace_corner_no].opposite());
}
}
else
{
if (p_shove_to_the_left)
{
Line start_closing_line = new Line(shrinked_line_segment.b.round(), perpendicular_direction);
shove_line_segment =
new LineSegment(start_closing_line, trace_polyline.arr[trace_corner_no + 1].opposite(),
trace_polyline.arr[trace_corner_no].opposite());
}
else
{
Line start_closing_line = new Line(shrinked_line_segment.a.round(), perpendicular_direction);
shove_line_segment =
new LineSegment(start_closing_line, trace_polyline.arr[trace_corner_no + 1],
trace_polyline.arr[trace_corner_no + 2]);
}
}
}
int trace_half_width = p_ctrl.trace_half_width[trace_layer];
int [] net_no_arr = new int[1];
net_no_arr[0] = p_ctrl.net_no;
double shove_width =
p_board.check_trace_segment(shove_line_segment, trace_layer, net_no_arr, trace_half_width,
p_ctrl.trace_clearance_class_no, true);
boolean segment_shortened = false;
if (shove_width < Integer.MAX_VALUE)
{
// shorten shove_line_segment
shove_width = shove_width - 1;
if (shove_width <= 0)
{
return true;
}
shove_line_segment = shove_line_segment.change_length_approx(shove_width);
segment_shortened = true;
}
FloatPoint from_corner = shove_line_segment.start_point_approx();
FloatPoint to_corner = shove_line_segment.end_point_approx();
boolean segment_ist_point = from_corner.distance_square(to_corner) < 0.1;
if (!segment_ist_point)
{
shove_width = ShoveTraceAlgo.check(p_board, shove_line_segment, p_shove_to_the_left, trace_layer, net_no_arr, trace_half_width,
p_ctrl.trace_clearance_class_no, p_ctrl.max_shove_trace_recursion_depth, p_ctrl.max_shove_via_recursion_depth);
if (shove_width <= 0)
{
return true;
}
}
// Put the doors on this side of the room into p_to_door_list with
if (segment_shortened)
{
shove_width = Math.min(shove_width, from_corner.distance(to_corner));
}
Line shove_line = shove_line_segment.get_line();
// From_door_compare_distance is used to check, that a door is between from_door and the end point
// of the shove line.
double from_door_compare_distance;
if (from_door.dimension == 2 || segment_ist_point)
{
from_door_compare_distance = Double.MAX_VALUE;
}
else
{
from_door_compare_distance = to_corner.distance_square(from_door_shape.corner_approx(0));
}
for (ExpansionDoor curr_door : room_doors)
{
if (curr_door == from_door)
{
continue;
}
if (curr_door.first_room instanceof ObstacleExpansionRoom &&
curr_door.second_room instanceof ObstacleExpansionRoom)
{
Item first_room_item = ((ObstacleExpansionRoom)curr_door.first_room).get_item();
Item second_room_item = ((ObstacleExpansionRoom)curr_door.second_room).get_item();
if (first_room_item != second_room_item)
{
// there may be topological problems at a trace fork
continue;
}
}
TileShape curr_door_shape = curr_door.get_shape();
if (curr_door.dimension == 2 && shove_width >= Integer.MAX_VALUE)
{
boolean add_link_door = curr_door_shape.contains(to_corner);
if (add_link_door)
{
FloatLine[] line_sections = curr_door.get_section_segments(compensated_trace_half_width);
p_to_door_list.add(new DoorSection(curr_door, 0, line_sections[0]));
}
continue;
}
else if (!segment_ist_point)
{
// now curr_door is 1-dimensional
// check, that curr_door is on the same border_line as p_from_door.
FloatLine curr_door_segment = curr_door_shape.diagonal_corner_segment();
if (curr_door_segment == null)
{
if (p_board.get_test_level() == eu.mihosoft.freerouting.board.TestLevel.ALL_DEBUGGING_OUTPUT)
{
System.out.println("MazeShoveTraceAlgo.check_shove_trace_line: door shape is empty");
}
continue;
}
Side start_corner_side_of_trace_line = shove_line.side_of(curr_door_segment.a, 0);
Side end_corner_side_of_trace_line = shove_line.side_of(curr_door_segment.b, 0);
if (p_shove_to_the_left)
{
if (start_corner_side_of_trace_line != Side.ON_THE_LEFT || end_corner_side_of_trace_line != Side.ON_THE_LEFT)
{
continue;
}
}
else
{
if (start_corner_side_of_trace_line != Side.ON_THE_RIGHT || end_corner_side_of_trace_line != Side.ON_THE_RIGHT)
{
continue;
}
}
FloatLine curr_door_line = curr_door_shape.polar_line_segment(from_corner);
FloatPoint curr_door_nearest_corner;
if (curr_door_line.a.distance_square(from_corner) <= curr_door_line.b.distance_square(from_corner))
{
curr_door_nearest_corner = curr_door_line.a;
}
else
{
curr_door_nearest_corner = curr_door_line.b;
}
if (to_corner.distance_square(curr_door_nearest_corner) >= from_door_compare_distance)
{
// curr_door is not located into the direction of to_corner.
continue;
}
FloatPoint curr_door_projection = curr_door_nearest_corner.projection_approx(shove_line);
if (curr_door_projection.distance(from_corner) + compensated_trace_half_width <= shove_width)
{
FloatLine[] line_sections = curr_door.get_section_segments(compensated_trace_half_width);
for (int i = 0; i < line_sections.length; ++i)
{
FloatLine curr_line_section = line_sections[i];
FloatPoint curr_section_nearest_corner;
if (curr_line_section.a.distance_square(from_corner) <= curr_line_section.b.distance_square(from_corner))
{
curr_section_nearest_corner = curr_line_section.a;
}
else
{
curr_section_nearest_corner = curr_line_section.b;
}
FloatPoint curr_section_projection = curr_section_nearest_corner.projection_approx(shove_line);
if (curr_section_projection.distance(from_corner) <= shove_width)
{
p_to_door_list.add(new DoorSection(curr_door, i, curr_line_section));
}
}
}
}
}
return true;
}
/**
* Check if the endpoints of p_trace and p_from_item are maching, so that the
* shove can continue through a link door.
*/
private static boolean end_points_matching(eu.mihosoft.freerouting.board.PolylineTrace p_trace, Item p_from_item)
{
if (p_from_item == p_trace)
{
return true;
}
if (!p_trace.shares_net(p_from_item))
{
return false;
}
boolean points_matching;
if (p_from_item instanceof eu.mihosoft.freerouting.board.DrillItem)
{
Point from_center = ((eu.mihosoft.freerouting.board.DrillItem) p_from_item).get_center();
points_matching = from_center.equals(p_trace.first_corner()) || from_center.equals(p_trace.last_corner());
}
else if (p_from_item instanceof eu.mihosoft.freerouting.board.PolylineTrace)
{
eu.mihosoft.freerouting.board.PolylineTrace from_trace = (eu.mihosoft.freerouting.board.PolylineTrace) p_from_item;
points_matching = p_trace.first_corner().equals(from_trace.first_corner()) ||
p_trace.first_corner().equals(from_trace.last_corner()) ||
p_trace.last_corner().equals(from_trace.first_corner()) ||
p_trace.last_corner().equals(from_trace.last_corner());
}
else
{
points_matching = false;
}
return points_matching;
}
public static class DoorSection
{
DoorSection(ExpansionDoor p_door, int p_section_no, FloatLine p_section_line)
{
door = p_door;
section_no = p_section_no;
section_line = p_section_line;
}
final ExpansionDoor door;
final int section_no;
final FloatLine section_line;
}
}