/*
* 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.
*/
package board;
import datastructures.TimeLimit;
import geometry.planar.ConvexShape;
import geometry.planar.Direction;
import geometry.planar.FloatPoint;
import geometry.planar.IntBox;
import geometry.planar.Line;
import geometry.planar.Point;
import geometry.planar.IntPoint;
import geometry.planar.Vector;
import geometry.planar.Polyline;
import geometry.planar.TileShape;
import geometry.planar.LineSegment;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
/**
* Contains internal auxiliary functions of class RoutingBoard
* for shoving traces
*
* @author Alfons Wirtz
*/
public class ShoveTraceAlgo
{
public ShoveTraceAlgo(RoutingBoard p_board)
{
board = p_board;
}
/**
* Checks if a shove with the input parameters is possible without clearance violations
* p_dir is used internally to prevent the check from bouncing back.
* Returns false, if the shove failed.
*/
public boolean check(TileShape p_trace_shape, CalcFromSide p_from_side,
Direction p_dir, int p_layer, int[] p_net_no_arr,
int p_cl_type, int p_max_recursion_depth, int p_max_via_recursion_depth,
int p_max_spring_over_recursion_depth, TimeLimit p_time_limit)
{
if (p_time_limit != null && p_time_limit.limit_exceeded())
{
return false;
}
if (p_trace_shape.is_empty())
{
System.out.println("ShoveTraceAux.check: p_trace_shape is empty");
return true;
}
if (!p_trace_shape.is_contained_in(board.get_bounding_box()))
{
this.board.set_shove_failing_obstacle(board.get_outline());
return false;
}
ShapeTraceEntries shape_entries =
new ShapeTraceEntries(p_trace_shape, p_layer, p_net_no_arr, p_cl_type, p_from_side, board);
ShapeSearchTree search_tree = this.board.search_tree_manager.get_default_tree();
Collection<Item> obstacles =
search_tree.overlapping_items_with_clearance(p_trace_shape, p_layer, new int[0], p_cl_type);
obstacles.removeAll(get_ignore_items_at_tie_pins(p_trace_shape, p_layer, p_net_no_arr));
boolean obstacles_shovable = shape_entries.store_items(obstacles, false, true);
if (!obstacles_shovable)
{
this.board.set_shove_failing_obstacle(shape_entries.get_found_obstacle());
return false;
}
int trace_piece_count = shape_entries.substitute_trace_count();
if (shape_entries.stack_depth() > 1)
{
this.board.set_shove_failing_obstacle(shape_entries.get_found_obstacle());
return false;
}
double shape_radius = 0.5 * p_trace_shape.bounding_box().min_width();
// check, if the obstacle vias can be shoved
for (Via curr_shove_via : shape_entries.shove_via_list)
{
if (curr_shove_via.shares_net_no(p_net_no_arr))
{
continue;
}
if (p_max_via_recursion_depth <= 0)
{
this.board.set_shove_failing_obstacle(curr_shove_via);
return false;
}
FloatPoint curr_shove_via_center = curr_shove_via.get_center().to_float();
IntPoint[] try_via_centers =
MoveDrillItemAlgo.try_shove_via_points(p_trace_shape, p_layer, curr_shove_via, p_cl_type,
true, board);
double max_dist = 0.5 * curr_shove_via.get_shape_on_layer(p_layer).bounding_box().max_width() + shape_radius;
double max_dist_square = max_dist * max_dist;
boolean shove_via_ok = false;
for (int i = 0; i < try_via_centers.length; ++i)
{
if (i == 0 || curr_shove_via_center.distance_square(try_via_centers[i].to_float()) <= max_dist_square)
{
Vector delta = try_via_centers[i].difference_by(curr_shove_via.get_center());
Collection<Item> ignore_items = new java.util.LinkedList<Item>();
if (MoveDrillItemAlgo.check(curr_shove_via, delta, p_max_recursion_depth,
p_max_via_recursion_depth - 1, ignore_items, this.board, p_time_limit))
{
shove_via_ok = true;
break;
}
}
}
if (!shove_via_ok)
{
return false;
}
}
if (trace_piece_count == 0)
{
return true;
}
if (p_max_recursion_depth <= 0)
{
this.board.set_shove_failing_obstacle(shape_entries.get_found_obstacle());
return false;
}
boolean is_orthogonal_mode = p_trace_shape instanceof IntBox;
for (;;)
{
PolylineTrace curr_substitute_trace =
shape_entries.next_substitute_trace_piece();
if (curr_substitute_trace == null)
{
break;
}
if (p_max_spring_over_recursion_depth > 0)
{
Polyline new_polyline = spring_over(curr_substitute_trace.polyline(),
curr_substitute_trace.get_compensated_half_width(search_tree), p_layer, curr_substitute_trace.net_no_arr,
curr_substitute_trace.clearance_class_no(), false, p_max_spring_over_recursion_depth, null);
if (new_polyline == null)
{
// spring_over did not work
return false;
}
if (new_polyline != curr_substitute_trace.polyline())
{
// spring_over changed something
--p_max_spring_over_recursion_depth;
curr_substitute_trace.change(new_polyline);
}
}
for (int i = 0; i < curr_substitute_trace.tile_shape_count(); ++i)
{
Direction curr_dir = curr_substitute_trace.polyline().arr[i + 1].direction();
boolean is_in_front = p_dir == null || p_dir.equals(curr_dir);
if (is_in_front)
{
CalcShapeAndFromSide curr =
new CalcShapeAndFromSide(curr_substitute_trace, i, is_orthogonal_mode, true);
if (!this.check(curr.shape, curr.from_side, curr_dir, p_layer, curr_substitute_trace.net_no_arr,
curr_substitute_trace.clearance_class_no(),
p_max_recursion_depth - 1, p_max_via_recursion_depth,
p_max_spring_over_recursion_depth, p_time_limit))
{
return false;
}
}
}
}
return true;
}
/**
* Checks if a shove with the input parameters is possible without clearance violations
* The result is the maximum lenght of a trace from the start of the line segment to the end of
* the line segment, for wich the algoritm succeedes.
* If the algorithm succeedes completely, the result will be equal to Integer.MAX_VALUE.
*/
public static double check(RoutingBoard p_board, LineSegment p_line_segment, boolean p_shove_to_the_left, int p_layer, int[] p_net_no_arr, int p_trace_half_width,
int p_cl_type, int p_max_recursion_depth, int p_max_via_recursion_depth)
{
ShapeSearchTree search_tree = p_board.search_tree_manager.get_default_tree();
if (search_tree.is_clearance_compensation_used())
{
p_trace_half_width += search_tree.clearance_compensation_value(p_cl_type, p_layer);
}
TileShape[] trace_shapes = p_line_segment.to_polyline().offset_shapes(p_trace_half_width);
if (trace_shapes.length != 1)
{
System.out.println("ShoveTraceAlgo.check: trace_shape count 1 expected");
return 0;
}
TileShape trace_shape = trace_shapes[0];
if (trace_shape.is_empty())
{
System.out.println("ShoveTraceAlgo.check: trace_shape is empty");
return 0;
}
if (!trace_shape.is_contained_in(p_board.get_bounding_box()))
{
return 0;
}
CalcFromSide from_side = new CalcFromSide(p_line_segment, trace_shape, p_shove_to_the_left);
ShapeTraceEntries shape_entries =
new ShapeTraceEntries(trace_shape, p_layer, p_net_no_arr, p_cl_type, from_side, p_board);
Collection<Item> obstacles =
search_tree.overlapping_items_with_clearance(trace_shape, p_layer, new int[0], p_cl_type);
boolean obstacles_shovable = shape_entries.store_items(obstacles, false, true);
if (!obstacles_shovable || shape_entries.trace_tails_in_shape())
{
return 0;
}
int trace_piece_count = shape_entries.substitute_trace_count();
if (shape_entries.stack_depth() > 1)
{
return 0;
}
FloatPoint start_corner_appprox = p_line_segment.start_point_approx();
FloatPoint end_corner_appprox = p_line_segment.end_point_approx();
double segment_length = end_corner_appprox.distance(start_corner_appprox);
rules.ClearanceMatrix cl_matrix = p_board.rules.clearance_matrix;
double result = Integer.MAX_VALUE;
// check, if the obstacle vias can be shoved
for (Via curr_shove_via : shape_entries.shove_via_list)
{
if (curr_shove_via.shares_net_no(p_net_no_arr))
{
continue;
}
boolean shove_via_ok = false;
if (p_max_via_recursion_depth > 0)
{
IntPoint[] new_via_center =
MoveDrillItemAlgo.try_shove_via_points(trace_shape, p_layer, curr_shove_via, p_cl_type,
false, p_board);
if (new_via_center.length <= 0)
{
return 0;
}
Vector delta = new_via_center[0].difference_by(curr_shove_via.get_center());
Collection<Item> ignore_items = new java.util.LinkedList<Item>();
shove_via_ok = MoveDrillItemAlgo.check(curr_shove_via, delta, p_max_recursion_depth,
p_max_via_recursion_depth - 1, ignore_items, p_board, null);
}
if (!shove_via_ok)
{
FloatPoint via_center_appprox = curr_shove_via.get_center().to_float();
double projection = start_corner_appprox.scalar_product(end_corner_appprox, via_center_appprox);
projection /= segment_length;
IntBox via_box = curr_shove_via.get_tree_shape_on_layer(search_tree, p_layer).bounding_box();
double via_radius = 0.5 * via_box.max_width();
double curr_ok_lenght = projection - via_radius - p_trace_half_width;
if (!search_tree.is_clearance_compensation_used())
{
curr_ok_lenght -= cl_matrix.value(p_cl_type, curr_shove_via.clearance_class_no(), p_layer);
}
if (curr_ok_lenght <= 0)
{
return 0;
}
result = Math.min(result, curr_ok_lenght);
}
}
if (trace_piece_count == 0)
{
return result;
}
if (p_max_recursion_depth <= 0)
{
return 0;
}
Direction line_direction = p_line_segment.get_line().direction();
for (;;)
{
PolylineTrace curr_substitute_trace =
shape_entries.next_substitute_trace_piece();
if (curr_substitute_trace == null)
{
break;
}
for (int i = 0; i < curr_substitute_trace.tile_shape_count(); ++i)
{
LineSegment curr_line_segment = new LineSegment(curr_substitute_trace.polyline(), i + 1);
if (p_shove_to_the_left)
{
// swap the line segmment to get the corredct shove length
// in case it is smmaller than the length of the whole line segmment.
curr_line_segment = curr_line_segment.opposite();
}
boolean is_in_front = curr_line_segment.get_line().direction().equals(line_direction);
if (is_in_front)
{
double shove_ok_length = check(p_board, curr_line_segment, p_shove_to_the_left, p_layer, curr_substitute_trace.net_no_arr,
curr_substitute_trace.get_half_width(), curr_substitute_trace.clearance_class_no(),
p_max_recursion_depth - 1, p_max_via_recursion_depth);
if (shove_ok_length < Integer.MAX_VALUE)
{
if (shove_ok_length <= 0)
{
return 0;
}
double projection =
Math.min(start_corner_appprox.scalar_product(end_corner_appprox, curr_line_segment.start_point_approx()),
start_corner_appprox.scalar_product(end_corner_appprox, curr_line_segment.end_point_approx()));
projection /= segment_length;
double curr_ok_length = shove_ok_length + projection - p_trace_half_width - curr_substitute_trace.get_half_width();
if (search_tree.is_clearance_compensation_used())
{
curr_ok_length -= search_tree.clearance_compensation_value(curr_substitute_trace.clearance_class_no(), p_layer);
}
else
{
curr_ok_length -= cl_matrix.value(p_cl_type, curr_substitute_trace.clearance_class_no(), p_layer);
}
if (curr_ok_length <= 0)
{
return 0;
}
result = Math.min(curr_ok_length, result);
}
break;
}
}
}
return result;
}
/**
* Puts in a trace segment with the input parameters and
* shoves obstacles out of the way. If the shove does not work,
* the database may be damaged. To prevent this, call check first.
*/
public boolean insert(TileShape p_trace_shape, CalcFromSide p_from_side, int p_layer, int[] p_net_no_arr,
int p_cl_type, Collection<Item> p_ignore_items,
int p_max_recursion_depth, int p_max_via_recursion_depth, int p_max_spring_over_recursion_depth)
{
if (p_trace_shape.is_empty())
{
System.out.println("ShoveTraceAux.insert: p_trace_shape is empty");
return true;
}
if (!p_trace_shape.is_contained_in(board.get_bounding_box()))
{
this.board.set_shove_failing_obstacle(board.get_outline());
return false;
}
if (!MoveDrillItemAlgo.shove_vias(p_trace_shape, p_from_side, p_layer, p_net_no_arr, p_cl_type,
p_ignore_items, p_max_recursion_depth, p_max_via_recursion_depth, true, this.board))
{
return false;
}
ShapeTraceEntries shape_entries =
new ShapeTraceEntries(p_trace_shape, p_layer, p_net_no_arr, p_cl_type, p_from_side, board);
ShapeSearchTree search_tree = this.board.search_tree_manager.get_default_tree();
Collection<Item> obstacles =
search_tree.overlapping_items_with_clearance(p_trace_shape, p_layer, new int[0], p_cl_type);
obstacles.removeAll(get_ignore_items_at_tie_pins(p_trace_shape, p_layer, p_net_no_arr));
boolean obstacles_shovable = shape_entries.store_items(obstacles, false, true);
if (!shape_entries.shove_via_list.isEmpty())
{
obstacles_shovable = false;
this.board.set_shove_failing_obstacle(shape_entries.shove_via_list.iterator().next());
return false;
}
if (!obstacles_shovable)
{
this.board.set_shove_failing_obstacle(shape_entries.get_found_obstacle());
return false;
}
int trace_piece_count = shape_entries.substitute_trace_count();
if (trace_piece_count == 0)
{
return true;
}
if (p_max_recursion_depth <= 0)
{
this.board.set_shove_failing_obstacle(shape_entries.get_found_obstacle());
return false;
}
boolean tails_exist_before = board.contains_trace_tails(obstacles, p_net_no_arr);
shape_entries.cutout_traces(obstacles);
boolean is_orthogonal_mode = p_trace_shape instanceof IntBox;
for (;;)
{
PolylineTrace curr_substitute_trace =
shape_entries.next_substitute_trace_piece();
if (curr_substitute_trace == null)
{
break;
}
if (curr_substitute_trace.first_corner().equals(curr_substitute_trace.last_corner()))
{
continue;
}
if (p_max_spring_over_recursion_depth > 0)
{
Polyline new_polyline = spring_over(curr_substitute_trace.polyline(),
curr_substitute_trace.get_compensated_half_width(search_tree), p_layer, curr_substitute_trace.net_no_arr,
curr_substitute_trace.clearance_class_no(), false, p_max_spring_over_recursion_depth, null);
if (new_polyline == null)
{
// spring_over did not work
return false;
}
if (new_polyline != curr_substitute_trace.polyline())
{
// spring_over changed something
--p_max_spring_over_recursion_depth;
curr_substitute_trace.change(new_polyline);
}
}
int[] curr_net_no_arr = curr_substitute_trace.net_no_arr;
for (int i = 0; i < curr_substitute_trace.tile_shape_count(); ++i)
{
CalcShapeAndFromSide curr =
new CalcShapeAndFromSide(curr_substitute_trace, i, is_orthogonal_mode, false);
if (!this.insert(curr.shape, curr.from_side, p_layer, curr_net_no_arr, curr_substitute_trace.clearance_class_no(),
p_ignore_items, p_max_recursion_depth - 1, p_max_via_recursion_depth, p_max_spring_over_recursion_depth))
{
return false;
}
}
for (int i = 0; i < curr_substitute_trace.corner_count(); ++i)
{
board.join_changed_area(
curr_substitute_trace.polyline().corner_approx(i), p_layer);
}
Point[] end_corners = null;
if (!tails_exist_before)
{
end_corners = new Point[2];
end_corners[0] = curr_substitute_trace.first_corner();
end_corners[1] = curr_substitute_trace.last_corner();
}
board.insert_item(curr_substitute_trace);
curr_substitute_trace.normalize(board.changed_area.get_area(p_layer));
if (!tails_exist_before)
{
for (int i = 0; i < 2; ++i)
{
Trace tail = board.get_trace_tail(end_corners[i], p_layer, curr_net_no_arr);
if (tail != null)
{
board.remove_items(tail.get_connection_items(Item.StopConnectionOption.VIA), false);
for (int curr_net_no : curr_net_no_arr)
{
board.combine_traces(curr_net_no);
}
}
}
}
}
return true;
}
Collection<Item> get_ignore_items_at_tie_pins(TileShape p_trace_shape, int p_layer, int[] p_net_no_arr)
{
Collection<SearchTreeObject> overlaps = this.board.overlapping_objects(p_trace_shape, p_layer);
Set<Item> result = new java.util.TreeSet<Item>();
for (SearchTreeObject curr_object : overlaps)
{
if (curr_object instanceof Pin)
{
Pin curr_pin = (Pin) curr_object;
if (curr_pin.shares_net_no(p_net_no_arr))
{
result.addAll(curr_pin.get_all_contacts(p_layer));
}
}
}
return result;
}
/**
* Checks, if there are obstacle in the way of p_polyline and tries
* to wrap the polyline trace around these obstacles in counterclock sense.
* Returns null, if that is not possible.
* Returns p_polyline, if there were no obstacles
* If p_contact_pins != null, all pins not contained in p_contact_pins are
* regarded as obstacles, even if they are of the own net.
*/
private Polyline spring_over(Polyline p_polyline, int p_half_width,
int p_layer, int[] p_net_no_arr, int p_cl_type, boolean p_over_connected_pins,
int p_recursion_depth, Set<Pin> p_contact_pins)
{
Item found_obstacle = null;
IntBox found_obstacle_bounding_box = null;
ShapeSearchTree search_tree = this.board.search_tree_manager.get_default_tree();
int[] check_net_no_arr;
if (p_contact_pins == null)
{
check_net_no_arr = p_net_no_arr;
}
else
{
check_net_no_arr = new int[0];
}
for (int i = 0; i < p_polyline.arr.length - 2; ++i)
{
TileShape curr_shape = p_polyline.offset_shape(p_half_width, i);
Collection<Item> obstacles = search_tree.overlapping_items_with_clearance(curr_shape, p_layer, check_net_no_arr, p_cl_type);
Iterator<Item> it = obstacles.iterator();
while (it.hasNext())
{
Item curr_item = it.next();
boolean is_obstacle;
if (curr_item.shares_net_no(p_net_no_arr))
{
// to avoid acid traps
is_obstacle = curr_item instanceof Pin && p_contact_pins != null &&
!p_contact_pins.contains(curr_item);
}
else if (curr_item instanceof ConductionArea)
{
is_obstacle = ((ConductionArea) curr_item).get_is_obstacle();
}
else if (curr_item instanceof ViaObstacleArea || curr_item instanceof ComponentObstacleArea)
{
is_obstacle = false;
}
else if (curr_item instanceof PolylineTrace)
{
if (curr_item.is_shove_fixed())
{
is_obstacle = true;
if (curr_item instanceof PolylineTrace)
{
// check for a shove fixed trace exit stub, which has to be be ignored at a tie pin.
Collection<Item> curr_contacts = curr_item.get_normal_contacts();
for (Item curr_contact : curr_contacts)
{
if (curr_contact.shares_net_no(p_net_no_arr))
{
is_obstacle = false;
}
}
}
}
else
{
// a unfixed trace can be pushed aside eventually
is_obstacle = false;
}
}
else
{
// a unfixed via can be pushed aside eventually
is_obstacle = !curr_item.is_route();
}
if (is_obstacle)
{
if (found_obstacle == null)
{
found_obstacle = curr_item;
found_obstacle_bounding_box = curr_item.bounding_box();
}
else if (found_obstacle != curr_item)
{
// check, if 1 obstacle is contained in the other obstacle and take
// the bigger obstacle in this case.
// That may happen in case of fixed vias inside of pins.
IntBox curr_item_bounding_box = curr_item.bounding_box();
if (found_obstacle_bounding_box.intersects(curr_item_bounding_box))
{
if (curr_item_bounding_box.contains(found_obstacle_bounding_box))
{
found_obstacle = curr_item;
found_obstacle_bounding_box = curr_item_bounding_box;
}
else if (!found_obstacle_bounding_box.contains(curr_item_bounding_box))
{
return null;
}
}
}
}
}
if (found_obstacle != null)
{
break;
}
}
if (found_obstacle == null)
{
// no obstacle in the way, nothing to do
return p_polyline;
}
if (p_recursion_depth <= 0 || found_obstacle instanceof BoardOutline || (found_obstacle instanceof Trace && !found_obstacle.is_shove_fixed()))
{
this.board.set_shove_failing_obstacle(found_obstacle);
return null;
}
boolean try_spring_over = true;
if (!p_over_connected_pins)
{
// Check if the obstacle has a trace contact on p_layer
Collection<Item> contacts_on_layer = found_obstacle.get_all_contacts(p_layer);
for (Item curr_contact : contacts_on_layer)
{
if (curr_contact instanceof Trace)
{
try_spring_over = false;
break;
}
}
}
ConvexShape obstacle_shape = null;
if (try_spring_over)
{
if (found_obstacle instanceof ObstacleArea || found_obstacle instanceof Trace)
{
if (found_obstacle.tree_shape_count(search_tree) == 1)
{
obstacle_shape = found_obstacle.get_tree_shape(search_tree, 0);
}
else
{
try_spring_over = false;
}
}
else if (found_obstacle instanceof DrillItem)
{
DrillItem found_drill_item = (DrillItem) found_obstacle;
obstacle_shape = (found_drill_item.get_tree_shape_on_layer(search_tree, p_layer));
}
}
if (!try_spring_over)
{
this.board.set_shove_failing_obstacle(found_obstacle);
return null;
}
TileShape offset_shape;
if (search_tree.is_clearance_compensation_used())
{
int offset = p_half_width + 1;
offset_shape = (TileShape) obstacle_shape.enlarge(offset);
}
else
{
// enlarge the shape in 2 steps for symmetry reasons
int offset = p_half_width + 1;
double half_cl_offset = 0.5 * board.clearance_value(found_obstacle.clearance_class_no(), p_cl_type, p_layer);
offset_shape = (TileShape) obstacle_shape.enlarge(offset + half_cl_offset);
offset_shape = (TileShape) offset_shape.enlarge(half_cl_offset);
}
if (this.board.rules.get_trace_angle_restriction() == AngleRestriction.NINETY_DEGREE)
{
offset_shape = offset_shape.bounding_box();
}
else if (this.board.rules.get_trace_angle_restriction() == AngleRestriction.FORTYFIVE_DEGREE)
{
offset_shape = offset_shape.bounding_octagon();
}
if (offset_shape.contains_inside(p_polyline.first_corner()) || offset_shape.contains_inside(p_polyline.last_corner()))
{
// can happen with clearance compensation off because of asymmetry in calculations with the offset shapes
this.board.set_shove_failing_obstacle(found_obstacle);
return null;
}
int[][] entries = offset_shape.entrance_points(p_polyline);
if (entries.length == 0)
{
return p_polyline; // no obstacle
}
if (entries.length <
2)
{
this.board.set_shove_failing_obstacle(found_obstacle);
return null;
}
Polyline[] pieces = offset_shape.cutout(p_polyline); // build a circuit around the offset_shape in counter clock sense
// from the first intersection point to the second intersection point
int first_intersection_side_no = entries[0][1];
int last_intersection_side_no = entries[entries.length - 1][1];
int first_intersection_line_no = entries[0][0];
int last_intersection_line_no = entries[entries.length - 1][0];
int side_diff = last_intersection_side_no - first_intersection_side_no;
if (side_diff <
0)
{
side_diff += offset_shape.border_line_count();
}
else if (side_diff == 0)
{
FloatPoint compare_corner = offset_shape.corner_approx(first_intersection_side_no);
FloatPoint first_intersection = p_polyline.arr[first_intersection_line_no].intersection_approx(offset_shape.border_line(first_intersection_side_no));
FloatPoint second_intersection = p_polyline.arr[last_intersection_line_no].intersection_approx(offset_shape.border_line(last_intersection_side_no));
if (compare_corner.distance(second_intersection) < compare_corner.distance(first_intersection))
{
side_diff += offset_shape.border_line_count();
}
}
Line[] substitute_lines = new Line[side_diff + 3];
substitute_lines[0] = p_polyline.arr[first_intersection_line_no];
int curr_edge_line_no = first_intersection_side_no;
for (int i = 1;
i <= side_diff + 1;
++i)
{
substitute_lines[i] = offset_shape.border_line(curr_edge_line_no);
if (curr_edge_line_no == offset_shape.border_line_count() - 1)
{
curr_edge_line_no = 0;
}
else
{
++curr_edge_line_no;
}
}
substitute_lines[side_diff + 2] = p_polyline.arr[last_intersection_line_no];
Polyline substitute_polyline = new Polyline(substitute_lines);
Polyline result = substitute_polyline;
if (pieces.length > 0)
{
result = pieces[0].combine(substitute_polyline);
}
if (pieces.length > 1)
{
result = result.combine(pieces[1]);
}
return spring_over(result, p_half_width, p_layer, p_net_no_arr, p_cl_type, p_over_connected_pins,
p_recursion_depth - 1, p_contact_pins);
}
/**
* Checks, if there are obstacle in the way of p_polyline and tries
* to wrap the polyline trace around these obstacles.
* Returns null, if that is not possible.
* Returns p_polyline, if there were no obstacles
* This function looks contrary to the previous function for the shortest
* way around the obstaccles.
* If p_contact_pins != null, all pins not contained in p_contact_pins are
* regarded as obstacles, even if they are of the own net.
*/
Polyline spring_over_obstacles(
Polyline p_polyline, int p_half_width, int p_layer, int[] p_net_no_arr,
int p_cl_type, Set<Pin> p_contact_pins)
{
final int c_max_spring_over_recursion_depth = 20;
Polyline counter_clock_wise_result = spring_over(p_polyline, p_half_width, p_layer, p_net_no_arr, p_cl_type,
true, c_max_spring_over_recursion_depth, p_contact_pins);
if (counter_clock_wise_result == p_polyline)
{
return p_polyline; // no obstacle
}
Polyline clock_wise_result = spring_over(p_polyline.reverse(), p_half_width, p_layer, p_net_no_arr, p_cl_type,
true, c_max_spring_over_recursion_depth, p_contact_pins);
Polyline result = null;
if (clock_wise_result != null && counter_clock_wise_result != null)
{
if (clock_wise_result.length_approx() <= counter_clock_wise_result.length_approx())
{
result = clock_wise_result.reverse();
}
else
{
result = counter_clock_wise_result;
}
}
else if (clock_wise_result != null)
{
result = clock_wise_result.reverse();
}
else if (counter_clock_wise_result != null)
{
result = counter_clock_wise_result;
}
return result;
}
private final RoutingBoard board;
}