409 lines
18 KiB
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;
|
|
}
|
|
}
|