/* * 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 14. Februar 2004, 07:55 */ package autoroute; import geometry.planar.FloatLine; import geometry.planar.FloatPoint; import geometry.planar.Side; import geometry.planar.TileShape; import java.util.Collection; import java.util.SortedSet; import java.util.LinkedList; import board.ShapeSearchTree; import board.AngleRestriction; import board.Item; import board.TestLevel; /** * Calculates from the backtrack list the location of the traces and vias, * which realize a connection found by the maze search algorithm. * * @author Alfons Wirtz */ class LocateFoundConnectionAlgoAnyAngle extends LocateFoundConnectionAlgo { /** Creates a new instance of LocateFoundConnectionAlgo */ protected LocateFoundConnectionAlgoAnyAngle(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) { super(p_maze_search_result, p_ctrl, p_search_tree, p_angle_restriction, p_ripped_item_list, p_test_level); } /** * Calculates a list with the next point of the trace under construction. * If the trace is completed, the result list will be empty. */ protected Collection<FloatPoint> calculate_next_trace_corners() { Collection<FloatPoint> result = new LinkedList<FloatPoint>(); if (this.current_to_door_index >= this.current_target_door_index) { if (this.current_to_door_index == this.current_target_door_index) { FloatPoint nearest_point = this.current_target_shape.nearest_point(this.current_from_point.round()).to_float(); ++this.current_to_door_index; result.add(nearest_point); } return result; } double trace_halfwidth_exact = this.ctrl.compensated_trace_half_width[this.current_trace_layer]; double trace_halfwidth_max = trace_halfwidth_exact + AutorouteEngine.TRACE_WIDTH_TOLERANCE; double trace_halfwidth_middle = trace_halfwidth_exact + c_tolerance; BacktrackElement curr_to_info = this.backtrack_array[this.current_to_door_index]; FloatPoint door_left_corner = calc_door_left_corner(curr_to_info); FloatPoint door_right_corner = calc_door_right_corner(curr_to_info); if (this.current_from_point.side_of(door_left_corner, door_right_corner) != Side.ON_THE_RIGHT) { // the door is already crossed at this.from_point if (this.current_from_point.scalar_product(this.previous_from_point, door_left_corner) >= 0) { // Also the left corner of the door is passed. // That may not be the case if the door line is crossed almost parallel. door_left_corner = null; } if(this.current_from_point.scalar_product(this.previous_from_point, door_right_corner) >= 0) { // Also the right corner of the door is passed. door_right_corner = null; } if (door_left_corner == null && door_right_corner == null) { // The door is completely passed. ++this.current_to_door_index; result.add(this.current_from_point); return result; } } // Calculate the visibility range for a trace line from current_from_point // through the interval from left_most_visible_point to right_most_visible_point, // by advancing the door index as far as possible, so that still somthing is visible. boolean end_of_trace = false; FloatPoint left_tangent_point = null; FloatPoint right_tangent_point = null; int new_door_ind = this.current_to_door_index; int left_ind = new_door_ind; int right_ind = new_door_ind; int curr_door_ind = this.current_to_door_index + 1; FloatPoint result_corner = null; // construct a maximum lenght straight line through the doors for (;;) { left_tangent_point = this.current_from_point.right_tangential_point(door_left_corner, trace_halfwidth_max); if (door_left_corner != null && left_tangent_point == null) { if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo.calculate_next_trace_corner: left tangent point is null"); } left_tangent_point = door_left_corner; } right_tangent_point = this.current_from_point.left_tangential_point(door_right_corner, trace_halfwidth_max); if (door_right_corner != null && right_tangent_point == null) { if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo.calculate_next_trace_corner: right tangent point is null"); } right_tangent_point = door_right_corner; } if (left_tangent_point != null && right_tangent_point != null && right_tangent_point.side_of(this.current_from_point, left_tangent_point) != Side.ON_THE_RIGHT) { // The gap between left_most_visible_point and right_most_visible_point ist to small // for a trace with the current half width. double left_corner_distance = door_left_corner.distance(this.current_from_point); double right_corner_distance = door_right_corner.distance(this.current_from_point); if ( left_corner_distance <= right_corner_distance) { new_door_ind = left_ind; result_corner = left_turn_next_corner(this.current_from_point, trace_halfwidth_max, door_left_corner, door_right_corner); } else { new_door_ind = right_ind; result_corner = right_turn_next_corner(this.current_from_point, trace_halfwidth_max, door_right_corner, door_left_corner); } break; } if (curr_door_ind >= this.current_target_door_index) { end_of_trace = true; break; } BacktrackElement next_to_info = this.backtrack_array[curr_door_ind]; FloatPoint next_left_corner = calc_door_left_corner(next_to_info); FloatPoint next_right_corner = calc_door_right_corner(next_to_info); if (this.current_from_point.side_of(next_left_corner, next_right_corner) != Side.ON_THE_RIGHT) { // the door may be already crossed at this.from_point if (door_left_corner == null && this.current_from_point.scalar_product(this.previous_from_point, next_left_corner) >= 0) { // Also the left corner of the door is passed. // That may not be the case if the door line is crossed almost parallel. next_left_corner = null; } if(door_right_corner == null && this.current_from_point.scalar_product(this.previous_from_point, next_right_corner) >= 0) { // Also the right corner of the door is passed. next_right_corner = null; } if (next_left_corner == null && next_right_corner == null) { // The door is completely passed. // Should not happen because the previous door was not passed compledtely. if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo.calculate_next_trace_corner: next door passed unexpected"); } ++this.current_to_door_index; result.add(this.current_from_point); return result; } } if (door_left_corner != null && door_right_corner != null) // otherwise the following side_of conditions may not be correct // even if all parameter points are defined { if (next_left_corner.side_of(this.current_from_point, door_right_corner) == Side.ON_THE_RIGHT) { // bend to the right new_door_ind = right_ind + 1; result_corner = right_turn_next_corner(this.current_from_point, trace_halfwidth_max, door_right_corner, next_left_corner); break; } if (next_right_corner.side_of(this.current_from_point, door_left_corner) == Side.ON_THE_LEFT) { // bend to the left new_door_ind = left_ind + 1; result_corner = left_turn_next_corner(this.current_from_point, trace_halfwidth_max, door_left_corner, next_right_corner); break; } } boolean visability_range_gets_smaller_on_the_right_side = (door_right_corner == null); if (door_right_corner != null && next_right_corner.side_of(this.current_from_point, door_right_corner) != Side.ON_THE_RIGHT) { FloatPoint curr_tangential_point = this.current_from_point.left_tangential_point(next_right_corner, trace_halfwidth_max); if (curr_tangential_point != null) { FloatLine check_line = new FloatLine(this.current_from_point, curr_tangential_point); if ( check_line.segment_distance(door_right_corner) >= trace_halfwidth_max) { visability_range_gets_smaller_on_the_right_side = true; } } } if (visability_range_gets_smaller_on_the_right_side) { // The visibility range gets smaller on the right side. door_right_corner = next_right_corner; right_ind = curr_door_ind; } boolean visability_range_gets_smaller_on_the_left_side = (door_left_corner == null); if (door_left_corner != null && next_left_corner.side_of(this.current_from_point, door_left_corner) != Side.ON_THE_LEFT) { FloatPoint curr_tangential_point = this.current_from_point.right_tangential_point(next_left_corner, trace_halfwidth_max); if (curr_tangential_point != null) { FloatLine check_line = new FloatLine(this.current_from_point, curr_tangential_point); if ( check_line.segment_distance(door_left_corner) >= trace_halfwidth_max) { visability_range_gets_smaller_on_the_left_side = true; } } } if (visability_range_gets_smaller_on_the_left_side) { // The visibility range gets smaller on the left side. door_left_corner = next_left_corner; left_ind = curr_door_ind; } ++curr_door_ind; } if (end_of_trace) { FloatPoint nearest_point = this.current_target_shape.nearest_point(this.current_from_point.round()).to_float(); result_corner = nearest_point; if (left_tangent_point != null && nearest_point.side_of(this.current_from_point, left_tangent_point) == Side.ON_THE_LEFT) { // The nearest target point is to the left of the visible range, add another corner new_door_ind = left_ind + 1; FloatPoint target_right_corner = this.current_target_shape.corner_approx(this.current_target_shape.index_of_right_most_corner(this.current_from_point)); FloatPoint curr_corner = right_left_tangential_point(this.current_from_point, target_right_corner, door_left_corner, trace_halfwidth_max); if (curr_corner != null) { result_corner = curr_corner; end_of_trace = false; } } else if (right_tangent_point != null && nearest_point.side_of(this.current_from_point, right_tangent_point) == Side.ON_THE_RIGHT) { // The nearest target point is to the right of the visible range, add another corner FloatPoint target_left_corner = this.current_target_shape.corner_approx(this.current_target_shape.index_of_left_most_corner(this.current_from_point)); new_door_ind = right_ind + 1; FloatPoint curr_corner = left_right_tangential_point(this.current_from_point, target_left_corner, door_right_corner, trace_halfwidth_max); if (curr_corner != null) { result_corner = curr_corner; end_of_trace = false; } } } if (end_of_trace) { new_door_ind = this.current_target_door_index; } // Check clearance violation with the previous door shapes // and correct them in this case. FloatLine check_line = new FloatLine(this.current_from_point, result_corner); int check_from_door_index = Math.max( this.current_to_door_index - 5 , this.current_from_door_index + 1); FloatPoint corrected_result = null; int corrected_door_ind = 0; for (int i = check_from_door_index; i < new_door_ind; ++i) { FloatPoint curr_left_corner = calc_door_left_corner(this.backtrack_array[i]); double curr_dist = check_line.segment_distance(curr_left_corner); if (Math.abs(curr_dist) < trace_halfwidth_middle) { FloatPoint curr_corrected_result = right_left_tangential_point(check_line.a, check_line.b, curr_left_corner, trace_halfwidth_max); if (curr_corrected_result != null) { if(corrected_result == null || curr_corrected_result.side_of(this.current_from_point, corrected_result) == Side.ON_THE_RIGHT) { corrected_door_ind = i; corrected_result = curr_corrected_result; } } } FloatPoint curr_right_corner = calc_door_right_corner(this.backtrack_array[i]); curr_dist = check_line.segment_distance(curr_right_corner); if (Math.abs(curr_dist) < trace_halfwidth_middle) { FloatPoint curr_corrected_result = left_right_tangential_point(check_line.a, check_line.b, curr_right_corner, trace_halfwidth_max); if (curr_corrected_result != null) { if(corrected_result == null || curr_corrected_result.side_of(this.current_from_point, corrected_result) == Side.ON_THE_LEFT) { corrected_door_ind = i; corrected_result = curr_corrected_result; } } } } if (corrected_result != null) { result_corner = corrected_result; new_door_ind = Math.max(corrected_door_ind, this.current_to_door_index); } this.current_to_door_index = new_door_ind; if(result_corner != null && result_corner != this.current_from_point) { result.add(result_corner); } return result; } /** * Calculates the left most corner of the shape of p_to_info.door * seen from the center of the common room with the previous door. */ private static FloatPoint calc_door_left_corner(BacktrackElement p_to_info) { CompleteExpansionRoom from_room = p_to_info.door.other_room(p_to_info.next_room); FloatPoint pole = from_room.get_shape().centre_of_gravity(); TileShape curr_to_door_shape = p_to_info.door.get_shape(); int left_most_corner_no = curr_to_door_shape.index_of_left_most_corner(pole); return curr_to_door_shape.corner_approx(left_most_corner_no); } /** * Calculates the right most corner of the shape of p_to_info.door * seen from the center of the common room with the previous door. */ private static FloatPoint calc_door_right_corner(BacktrackElement p_to_info) { CompleteExpansionRoom from_room = p_to_info.door.other_room(p_to_info.next_room); FloatPoint pole = from_room.get_shape().centre_of_gravity(); TileShape curr_to_door_shape = p_to_info.door.get_shape(); int right_most_corner_no = curr_to_door_shape.index_of_right_most_corner(pole); return curr_to_door_shape.corner_approx(right_most_corner_no); } /** * Calculates as first line the left side tangent from p_from_corner to * the circle with center p_to_corner and radius p_dist. * As second line the right side tangent from p_to_corner to the circle * with center p_next_corner and radius 2 * p_dist is constructed. * The second line is than translated by the distance p_dist to the left. * Returned is the intersection of the first and the second line. */ private FloatPoint right_turn_next_corner(FloatPoint p_from_corner, double p_dist, FloatPoint p_to_corner, FloatPoint p_next_corner) { FloatPoint curr_tangential_point = p_from_corner.left_tangential_point(p_to_corner, p_dist); if (curr_tangential_point == null) { if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo.right_turn_next_corner: left tangential point is null"); } return p_from_corner; } FloatLine first_line = new FloatLine(p_from_corner, curr_tangential_point); curr_tangential_point = p_to_corner.right_tangential_point(p_next_corner, 2 * p_dist + c_tolerance); if (curr_tangential_point == null) { if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo.right_turn_next_corner: right tangential point is null"); } return p_from_corner; } FloatLine second_line = new FloatLine(p_to_corner, curr_tangential_point); second_line = second_line.translate(p_dist); return first_line.intersection(second_line); } /** * Calculates as first line the right side tangent from p_from_corner to * the circle with center p_to_corner and radius p_dist. * As second line the left side tangent from p_to_corner to the circle * with center p_next_corner and radius 2 * p_dist is constructed. * The second line is than translated by the distance p_dist to the right. * Returned is the intersection of the first and the second line. */ private FloatPoint left_turn_next_corner(FloatPoint p_from_corner, double p_dist, FloatPoint p_to_corner, FloatPoint p_next_corner) { FloatPoint curr_tangential_point = p_from_corner.right_tangential_point(p_to_corner, p_dist); if (curr_tangential_point == null) { if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo.left_turn_next_corner: right tangential point is null"); } return p_from_corner; } FloatLine first_line = new FloatLine( p_from_corner, curr_tangential_point); curr_tangential_point = p_to_corner.left_tangential_point(p_next_corner, 2 * p_dist + c_tolerance); if (curr_tangential_point == null) { if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo.left_turn_next_corner: left tangential point is null"); } return p_from_corner; } FloatLine second_line = new FloatLine(p_to_corner, curr_tangential_point); second_line = second_line.translate(-p_dist); return first_line.intersection(second_line); } /** * Calculates the right tangential line from p_from_point and the * left tangential line from p_to_point to the circle * with center p_center and radius p_dist. * Returns the intersection of the 2 lines. */ private FloatPoint right_left_tangential_point(FloatPoint p_from_point, FloatPoint p_to_point, FloatPoint p_center, double p_dist) { FloatPoint curr_tangential_point = p_from_point.right_tangential_point(p_center, p_dist); if (curr_tangential_point == null) { if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo. right_left_tangential_point: right tangential point is null"); } return null; } FloatLine first_line = new FloatLine(p_from_point, curr_tangential_point); curr_tangential_point = p_to_point.left_tangential_point(p_center, p_dist); if (curr_tangential_point == null) { if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo. right_left_tangential_point: left tangential point is null"); } return null; } FloatLine second_line = new FloatLine(p_to_point, curr_tangential_point); return first_line.intersection(second_line); } /** * Calculates the left tangential line from p_from_point and the * right tangential line from p_to_point to the circle * with center p_center and radius p_dist. * Returns the intersection of the 2 lines. */ private FloatPoint left_right_tangential_point(FloatPoint p_from_point, FloatPoint p_to_point, FloatPoint p_center, double p_dist) { FloatPoint curr_tangential_point = p_from_point.left_tangential_point(p_center, p_dist); if (curr_tangential_point == null) { if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo. left_right_tangential_point: left tangential point is null"); } return null; } FloatLine first_line = new FloatLine(p_from_point, curr_tangential_point); curr_tangential_point = p_to_point.right_tangential_point(p_center, p_dist); if (curr_tangential_point == null) { if (this.test_level.ordinal() >= TestLevel.ALL_DEBUGGING_OUTPUT.ordinal()) { System.out.println("LocateFoundConnectionAlgo. left_right_tangential_point: right tangential point is null"); } return null; } FloatLine second_line = new FloatLine(p_to_point, curr_tangential_point); return first_line.intersection(second_line); } static private final double c_tolerance = 1.0; }