freerouting/src/main/java/eu/mihosoft/freerouting/board/PullTightAlgoAnyAngle.java

1238 lines
51 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.
*/
package eu.mihosoft.freerouting.board;
import eu.mihosoft.freerouting.datastructures.Stoppable;
import eu.mihosoft.freerouting.geometry.planar.Limits;
import eu.mihosoft.freerouting.datastructures.Signum;
import eu.mihosoft.freerouting.geometry.planar.Direction;
import eu.mihosoft.freerouting.geometry.planar.FloatPoint;
import eu.mihosoft.freerouting.geometry.planar.IntPoint;
import eu.mihosoft.freerouting.geometry.planar.Point;
import eu.mihosoft.freerouting.geometry.planar.Line;
import eu.mihosoft.freerouting.geometry.planar.Polyline;
import eu.mihosoft.freerouting.geometry.planar.Side;
import eu.mihosoft.freerouting.geometry.planar.TileShape;
/**
*
* Auxiliary class containing internal functions for pulling any angle traces tight.
*
*
* @author Alfons Wirtz
*/
class PullTightAlgoAnyAngle extends PullTightAlgo
{
PullTightAlgoAnyAngle(RoutingBoard p_board, int[] p_only_net_no_arr, Stoppable p_stoppable_thread, int p_time_limit,
Point p_keep_point, int p_keep_point_layer)
{
super(p_board, p_only_net_no_arr, p_stoppable_thread, p_time_limit, p_keep_point, p_keep_point_layer);
}
Polyline pull_tight(Polyline p_polyline)
{
Polyline new_result = avoid_acid_traps(p_polyline);
Polyline prev_result = null;
while (new_result != prev_result)
{
if (is_stop_requested())
{
break;
}
prev_result = new_result;
Polyline tmp = skip_segments_of_length_0(prev_result);
Polyline tmp0 = reduce_lines(tmp);
Polyline tmp1 = skip_lines(tmp0);
// I intended to replace reduce_corners by the previous 2
// functions, because with consecutive corners closer than
// 1 grid point reduce_corners may loop with smoothen_corners
// because of changing directions heavily.
// Unlike reduce_corners, the above 2 functions do not
// introduce new directions
Polyline tmp2 = reduce_corners(tmp1);
Polyline tmp3 = reposition_lines(tmp2);
new_result = smoothen_corners(tmp3);
}
return new_result;
}
// tries to reduce the corner count of p_polyline by replacing two consecutive
// lines by a line through IntPoints near the previous corner and the next
// corner, if that is possible without clearance violation.
private Polyline reduce_corners(Polyline p_polyline)
{
if (p_polyline.arr.length < 4)
{
return p_polyline;
}
int last_index = p_polyline.arr.length - 4;
Line [] new_lines = new Line [p_polyline.arr.length];
new_lines[0] = p_polyline.arr[0];
new_lines[1] = p_polyline.arr[1];
int new_line_index = 1;
boolean polyline_changed = false;
Line [] curr_lines = new Line[3];
for (int i = 0; i <= last_index; ++i)
{
boolean skip_line = false;
FloatPoint new_a = new_lines[new_line_index - 1].intersection_approx(new_lines[new_line_index]);
FloatPoint new_b = p_polyline.corner_approx(i + 2);
boolean in_clip_shape = curr_clip_shape == null ||
curr_clip_shape.contains(new_a) && curr_clip_shape.contains(new_b)
&& curr_clip_shape.contains(p_polyline.corner_approx(new_line_index));
if (in_clip_shape)
{
FloatPoint skip_corner =
new_lines[new_line_index].intersection_approx(p_polyline.arr[i + 2]);
curr_lines [1] = new Line(new_a.round(), new_b.round());
boolean ok = true;
if (new_line_index == 1)
{
if (!(p_polyline.first_corner() instanceof IntPoint))
{
// first corner must not be changed
ok = false;
}
else
{
Direction dir = curr_lines[1].direction();
curr_lines[0] =
Line.get_instance(p_polyline.first_corner(), dir.turn_45_degree(2));
}
}
else
{
curr_lines[0] = new_lines[new_line_index - 1];
}
if (i == last_index)
{
if (!(p_polyline.last_corner() instanceof IntPoint))
{
// last corner must not be changed
ok = false;
}
else
{
Direction dir = curr_lines[1].direction();
curr_lines[2] = Line.get_instance(p_polyline.last_corner(), dir.turn_45_degree(2));
}
}
else
{
curr_lines[2] = p_polyline.arr[i + 3];
}
// check, if the intersection of curr_lines[0] and curr_lines[1]
// is near new_a and the intersection of curr_lines[0] and
// curr_lines[1] and curr_lines[2] is near new_b.
// There may be numerical stability proplems with
// near parallel lines.
final double check_dist = 100;
if (ok)
{
FloatPoint check_is = curr_lines[0].intersection_approx(curr_lines[1]);
double dist = check_is.distance_square(new_a);
if (dist > check_dist)
{
ok = false;
}
}
if (ok)
{
FloatPoint check_is = curr_lines[1].intersection_approx(curr_lines[2]);
double dist = check_is.distance_square(new_b);
if (dist > check_dist)
{
ok = false;
}
}
if (ok && i == 1 && !(p_polyline.first_corner() instanceof IntPoint))
{
// There may be a connection to a trace.
// make shure that the second corner of the new polyline
// is on the same side of the trace as the third corner. (There may be splitting problems)
Point new_corner = curr_lines[0].intersection(curr_lines[1]);
if (new_corner.side_of(new_lines[0]) != p_polyline.corner(1).side_of(new_lines[0]))
{
ok = false;
}
}
if (ok && i == last_index - 1 && !(p_polyline.last_corner() instanceof IntPoint))
{
// There may be a connection to a trace.
// make shure that the second last corner of the new polyline
// is on the same side of the trace as the third last corner (There may be splitting problems)
Point new_corner = curr_lines[1].intersection(curr_lines[2]);
if (new_corner.side_of(new_lines[0]) !=
p_polyline.corner(p_polyline.corner_count() - 2).side_of(new_lines[0]))
{
ok = false;
}
}
Polyline curr_polyline = null;
if (ok)
{
curr_polyline = new Polyline(curr_lines);
if ( curr_polyline.arr.length != 3)
{
ok = false;
}
double length_before = skip_corner.distance(new_a) +
skip_corner.distance(new_b);
double length_after = curr_polyline.length_approx() + 1.5;
// 1.5 added because of possible inacurracy SQRT_2
// by twice rounding.
if (length_after >= length_before)
// May happen from rounding to integer.
// Prevent infinite loop.
{
ok = false;
}
}
if (ok)
{
TileShape shape_to_check = curr_polyline.offset_shape(curr_half_width, 0);
skip_line = board.check_trace_shape(shape_to_check,
curr_layer, curr_net_no_arr, curr_cl_type, this.contact_pins);
}
}
if (skip_line)
{
polyline_changed = true;
new_lines[new_line_index] = curr_lines[1];
if (new_line_index == 1)
{
// make the first line perpendicular to the current line
new_lines[0] = curr_lines [0];
}
if (i == last_index)
{
// make the last line perpendicular to the current line
++new_line_index;
new_lines[new_line_index] = curr_lines[2];
}
if (board.changed_area != null)
{
board.changed_area.join(new_a, curr_layer);
board.changed_area.join(new_b, curr_layer);
}
}
else
{
++new_line_index;
new_lines[new_line_index] = p_polyline.arr[i + 2];
if (i == last_index)
{
++new_line_index;
new_lines[new_line_index] = p_polyline.arr[i + 3];
}
}
if (new_lines[new_line_index].is_parallel(new_lines[new_line_index - 1]))
{
// skip line, if it is parallel to the previous one
--new_line_index;
}
}
if (!polyline_changed)
{
return p_polyline;
}
Line [] cleaned_new_lines = new Line [new_line_index + 1];
System.arraycopy(new_lines, 0, cleaned_new_lines, 0, cleaned_new_lines.length);
Polyline result = new Polyline(cleaned_new_lines);
return result;
}
/**
* tries to smoothen p_polyline by cutting of corners, if possible
*/
private Polyline smoothen_corners(Polyline p_polyline)
{
if (p_polyline.arr.length < 4)
{
return p_polyline;
}
boolean polyline_changed = false;
Line[] line_arr = new Line[p_polyline.arr.length];
System.arraycopy(p_polyline.arr, 0, line_arr, 0, line_arr.length);
for (int i = 0; i < line_arr.length - 3; ++i)
{
Line new_line = smoothen_corner(line_arr, i);
if (new_line != null)
{
polyline_changed = true;
// add the new line into the line array
Line[] tmp_lines = new Line[line_arr.length + 1];
System.arraycopy(line_arr, 0, tmp_lines, 0, i + 2);
tmp_lines [i + 2] = new_line;
System.arraycopy(line_arr, i + 2, tmp_lines, i + 3,
tmp_lines.length - (i + 3));
line_arr = tmp_lines;
++i;
}
}
if (!polyline_changed)
{
return p_polyline;
}
return new Polyline(line_arr);
}
/**
* tries to shorten p_polyline by relocating its lines
*/
Polyline reposition_lines(Polyline p_polyline)
{
if (p_polyline.arr.length < 5)
{
return p_polyline;
}
boolean polyline_changed = false;
Line[] line_arr = new Line[p_polyline.arr.length];
System.arraycopy(p_polyline.arr, 0, line_arr, 0, line_arr.length);
for (int i = 0; i < line_arr.length - 4; ++i)
{
Line new_line = reposition_line(line_arr, i);
if (new_line != null)
{
polyline_changed = true;
line_arr [i + 2] = new_line;
if (line_arr[i + 2].is_parallel(line_arr[i + 1]) ||
line_arr[i + 2].is_parallel(line_arr[i + 3]))
{
// calculation of corners not possible before skipping
// parallel lines
break;
}
}
}
if (!polyline_changed)
{
return p_polyline;
}
return new Polyline(line_arr);
}
/**
* tries to reduce te number of lines of p_polyline by moving
* lines parallel beyond the intersection of the next or privious lines.
*/
private Polyline reduce_lines(Polyline p_polyline)
{
if (p_polyline.arr.length < 6)
{
return p_polyline;
}
boolean polyline_changed = false;
Line[] line_arr = p_polyline.arr;
for (int i = 2; i < line_arr.length - 2; ++i)
{
FloatPoint prev_corner =
line_arr[i - 2].intersection_approx( line_arr[i - 1]);
FloatPoint next_corner =
line_arr[i +1 ].intersection_approx( line_arr[i + 2]);
boolean in_clip_shape = curr_clip_shape == null ||
curr_clip_shape.contains(prev_corner) &&
curr_clip_shape.contains(next_corner);
if (!in_clip_shape)
{
continue;
}
Line translate_line = line_arr [i];
double prev_dist = translate_line.signed_distance(prev_corner);
double next_dist = translate_line.signed_distance(next_corner);
if (Signum.of(prev_dist)!= Signum.of(next_dist))
// the 2 corners are on different sides of the translate_line
{
continue;
}
double translate_dist;
if (Math.abs(prev_dist) < Math.abs(next_dist))
{
translate_dist = prev_dist;
}
else
{
translate_dist = next_dist;
}
if (translate_dist == 0)
{
//line segment may have length 0
continue;
}
Side line_side = translate_line.side_of(prev_corner);
Line new_line = translate_line.translate(-translate_dist);
// make shure, we have crossed the nearest_corner;
int sign = Signum.as_int(translate_dist);
Side new_line_side_of_prev_corner = new_line.side_of(prev_corner);
Side new_line_side_of_next_corner = new_line.side_of(next_corner);
while (new_line_side_of_prev_corner == line_side &&
new_line_side_of_next_corner == line_side)
{
translate_dist += sign * 0.5;
new_line = translate_line.translate(-translate_dist);
new_line_side_of_prev_corner = new_line.side_of(prev_corner);
new_line_side_of_next_corner = new_line.side_of(next_corner);
}
int crossed_corners_before_count = 0;
int crossed_corners_after_count = 0;
if (new_line_side_of_prev_corner != line_side)
{
++crossed_corners_before_count;
}
if (new_line_side_of_next_corner != line_side)
{
++crossed_corners_after_count;
}
// check, that we havent crossed both corners
if (crossed_corners_before_count > 1 || crossed_corners_after_count > 1)
{
continue;
}
// check, that next_nearest_corner and nearest_corner are on
// different sides of new_line;
if (crossed_corners_before_count > 0)
{
if (i < 3)
{
continue;
}
FloatPoint prev_prev_corner =
line_arr[i - 3].intersection_approx( line_arr[i - 2]);
if (new_line.side_of(prev_prev_corner) != line_side)
{
continue;
}
}
if (crossed_corners_after_count > 0)
{
if (i >= line_arr.length - 3)
{
continue;
}
FloatPoint next_next_corner =
line_arr[i + 2 ].intersection_approx( line_arr[i + 3]);
if (new_line.side_of(next_next_corner) != line_side)
{
continue;
}
}
Line [] curr_lines = new Line[line_arr.length -
crossed_corners_before_count - crossed_corners_after_count];
int keep_before_ind = i - crossed_corners_before_count;
System.arraycopy(line_arr, 0, curr_lines, 0, keep_before_ind);
curr_lines [keep_before_ind] = new_line;
System.arraycopy(line_arr, i + 1 + crossed_corners_after_count, curr_lines,
keep_before_ind + 1, curr_lines.length - ( keep_before_ind + 1));
Polyline tmp = new Polyline( curr_lines);
boolean check_ok = false;
if (tmp.arr.length == curr_lines.length)
{
TileShape shape_to_check =
tmp.offset_shape(curr_half_width, keep_before_ind - 1);
check_ok = board.check_trace_shape(shape_to_check,
curr_layer, curr_net_no_arr, curr_cl_type, this.contact_pins);
}
if (check_ok)
{
if (board.changed_area != null)
{
board.changed_area.join(prev_corner, curr_layer);
board.changed_area.join(next_corner, curr_layer);
}
polyline_changed = true;
line_arr = curr_lines;
--i;
}
}
if (!polyline_changed)
{
return p_polyline;
}
return new Polyline(line_arr);
}
private Line smoothen_corner(Line[] p_line_arr, int p_start_no)
{
if ( p_line_arr.length - p_start_no < 4)
{
return null;
}
FloatPoint curr_corner =
p_line_arr[p_start_no + 1].intersection_approx(p_line_arr[p_start_no + 2]);
if (curr_clip_shape != null &&
!curr_clip_shape.contains(curr_corner))
{
return null;
}
double cosinus_angle =
p_line_arr[p_start_no + 1].cos_angle(p_line_arr[p_start_no + 2]);
if (cosinus_angle > c_max_cos_angle)
// lines are already nearly parallel, don't divide angle any further
// because of problems with numerical stability
{
return null;
}
FloatPoint prev_corner =
p_line_arr[p_start_no].intersection_approx( p_line_arr[p_start_no + 1]);
FloatPoint next_corner =
p_line_arr[p_start_no + 2].intersection_approx(p_line_arr[p_start_no + 3]);
// create a line approximately through curr_corner, whose
// direction is about the middle of the directions of the
// previous and the next line.
// Translations of this line are used to cut off the corner.
Direction prev_dir = p_line_arr[p_start_no + 1].direction();
Direction next_dir = p_line_arr[p_start_no + 2].direction();
Direction middle_dir = prev_dir.middle_approx(next_dir);
Line translate_line = Line.get_instance(curr_corner.round(), middle_dir);
double prev_dist = translate_line.signed_distance(prev_corner);
double next_dist = translate_line.signed_distance(next_corner);
FloatPoint nearest_point;
double max_translate_dist;
if (Math.abs(prev_dist) < Math.abs(next_dist))
{
nearest_point = prev_corner;
max_translate_dist = prev_dist;
}
else
{
nearest_point = next_corner;
max_translate_dist = next_dist;
}
if (Math.abs(max_translate_dist) < 1)
{
return null;
}
Line [] curr_lines = new Line[p_line_arr.length + 1];
System.arraycopy(p_line_arr, 0, curr_lines, 0, p_start_no + 2);
System.arraycopy(p_line_arr, p_start_no + 2, curr_lines,
p_start_no + 3, curr_lines.length - p_start_no - 3);
double translate_dist = max_translate_dist;
double delta_dist = max_translate_dist;
Side side_of_nearest_point = translate_line.side_of(nearest_point);
int sign = Signum.as_int(max_translate_dist);
Line result = null;
while (Math.abs(delta_dist) > this.min_translate_dist)
{
boolean check_ok = false;
Line new_line = translate_line.translate(-translate_dist);
Side new_line_side_of_nearest_point = new_line.side_of(nearest_point);
if (new_line_side_of_nearest_point == side_of_nearest_point
|| new_line_side_of_nearest_point == Side.COLLINEAR)
{
curr_lines [p_start_no + 2] = new_line;
Polyline tmp = new Polyline( curr_lines);
if (tmp.arr.length == curr_lines.length)
{
TileShape shape_to_check =
tmp.offset_shape(curr_half_width, p_start_no + 1);
check_ok = board.check_trace_shape(shape_to_check,
curr_layer, curr_net_no_arr, curr_cl_type, this.contact_pins);
}
delta_dist /= 2;
if (check_ok)
{
result = curr_lines[p_start_no + 2];
if (translate_dist == max_translate_dist)
{
// biggest possible change
break;
}
translate_dist += delta_dist;
}
else
{
translate_dist -= delta_dist;
}
}
else
// moved a little bit to far at the first time
// because of numerical inaccuracy
{
double shorten_value = sign * 0.5;
max_translate_dist -= shorten_value;
translate_dist -= shorten_value;
delta_dist -= shorten_value;
}
}
if (result == null)
{
return null;
}
if (board.changed_area != null)
{
FloatPoint new_prev_corner =
curr_lines[p_start_no].intersection_approx( curr_lines[p_start_no + 1]);
FloatPoint new_next_corner =
curr_lines[p_start_no + 3].intersection_approx( curr_lines[p_start_no + 4]);
board.changed_area.join(new_prev_corner, curr_layer);
board.changed_area.join(new_next_corner, curr_layer);
}
return result;
}
protected Line reposition_line(Line[] p_line_arr, int p_start_no)
{
if ( p_line_arr.length - p_start_no < 5)
{
return null;
}
if (curr_clip_shape != null)
// check, that the corners of the line to translate are inside
// the clip shape
{
for (int i = 1; i < 3; ++i)
{
FloatPoint curr_corner =
p_line_arr[p_start_no + i].intersection_approx(p_line_arr[p_start_no + i + 1]);
if(!curr_clip_shape.contains(curr_corner))
{
return null;
}
}
}
Line translate_line = p_line_arr[p_start_no + 2];
FloatPoint prev_corner =
p_line_arr[p_start_no].intersection_approx( p_line_arr[p_start_no + 1]);
FloatPoint next_corner =
p_line_arr[p_start_no + 3].intersection_approx(p_line_arr[p_start_no + 4]);
double prev_dist = translate_line.signed_distance(prev_corner);
int corners_skipped_before = 0;
int corners_skipped_after = 0;
final double c_epsilon = 0.001;
while (Math.abs(prev_dist) < c_epsilon)
// move also all lines trough the start corner of the line to translate
{
++corners_skipped_before;
int curr_no = p_start_no - corners_skipped_before;
if (curr_no < 0)
// the first corner is on the line to translate
{
return null;
}
prev_corner = p_line_arr[curr_no].intersection_approx( p_line_arr[curr_no + 1]);
prev_dist = translate_line.signed_distance(prev_corner);
}
double next_dist = translate_line.signed_distance(next_corner);
while (Math.abs(next_dist) < c_epsilon)
// move also all lines trough the end corner of the line to translate
{
++corners_skipped_after;
int curr_no = p_start_no + 3 + corners_skipped_after;
if (curr_no >= p_line_arr.length - 2)
// the last corner is on the line to translate
{
return null;
}
next_corner = p_line_arr[curr_no].intersection_approx( p_line_arr[curr_no + 1]);
next_dist = translate_line.signed_distance(next_corner);
}
if (Signum.of(prev_dist)!= Signum.of(next_dist))
// the 2 corners are at different sides of translate_line
{
return null;
}
FloatPoint nearest_point;
double max_translate_dist;
if (Math.abs(prev_dist) < Math.abs(next_dist))
{
nearest_point = prev_corner;
max_translate_dist = prev_dist;
}
else
{
nearest_point = next_corner;
max_translate_dist = next_dist;
}
Line [] curr_lines = new Line[p_line_arr.length];
System.arraycopy(p_line_arr, 0, curr_lines, 0, p_start_no + 2);
System.arraycopy(p_line_arr, p_start_no + 3,
curr_lines, p_start_no + 3, curr_lines.length - p_start_no - 3);
double translate_dist = max_translate_dist;
double delta_dist = max_translate_dist;
Side side_of_nearest_point = translate_line.side_of(nearest_point);
int sign = Signum.as_int(max_translate_dist);
Line result = null;
boolean first_time = true;
while (first_time || Math.abs(delta_dist) > this.min_translate_dist)
{
boolean check_ok = false;
Line new_line = translate_line.translate(-translate_dist);
if (first_time && Math.abs(translate_dist) < 1)
{
if (new_line.equals(translate_line))
{
// try the parallel line through the nearest_point
IntPoint rounded_nearest_point = nearest_point.round();
if (nearest_point.distance(rounded_nearest_point.to_float())
< Math.abs(translate_dist))
{
new_line = Line.get_instance(rounded_nearest_point,
translate_line.direction());
}
first_time = false;
}
if (new_line.equals(translate_line))
{
return null;
}
}
Side new_line_side_of_nearest_point = new_line.side_of(nearest_point);
if (new_line_side_of_nearest_point == side_of_nearest_point
|| new_line_side_of_nearest_point == Side.COLLINEAR)
{
first_time = false;
curr_lines [p_start_no + 2] = new_line;
// corners_skipped_before > 0 or corners_skipped_after > 0
// happens very rarely. But this handling seems to be
// important because there are situations which no other
// tightening function can solve. For example when 3 ore more
// consecutive corners are equal.
Line prev_translated_line = new_line;
for (int i = 0; i < corners_skipped_before; ++i)
// Translate the previous lines onto or past the
// intersection of new_line with the first untranslated line.
{
int prev_line_no = p_start_no + 1 - corners_skipped_before;
FloatPoint curr_prev_corner =
prev_translated_line.intersection_approx(curr_lines[prev_line_no]);
Line curr_translate_line = p_line_arr [p_start_no + 1 - i];
double curr_translate_dist = curr_translate_line.signed_distance(curr_prev_corner);
prev_translated_line = curr_translate_line.translate(-curr_translate_dist);
curr_lines[p_start_no + 1 - i] = prev_translated_line;
}
prev_translated_line = new_line;
for (int i = 0; i < corners_skipped_after; ++i)
// Translate the next lines onto or past the
// intersection of new_line with the first untranslated line.
{
int next_line_no = p_start_no + 3 + corners_skipped_after;
FloatPoint curr_next_corner =
prev_translated_line.intersection_approx(curr_lines[next_line_no]);
Line curr_translate_line = p_line_arr [p_start_no + 3 + i];
double curr_translate_dist = curr_translate_line.signed_distance(curr_next_corner);
prev_translated_line = curr_translate_line.translate(-curr_translate_dist);
curr_lines[p_start_no + 3 + i] = prev_translated_line;
}
Polyline tmp = new Polyline( curr_lines);
if (tmp.arr.length == curr_lines.length)
{
TileShape shape_to_check =
tmp.offset_shape(curr_half_width, p_start_no + 1);
check_ok = board.check_trace_shape(shape_to_check,
curr_layer, curr_net_no_arr, curr_cl_type, this.contact_pins);
}
delta_dist /= 2;
if (check_ok)
{
result = curr_lines[p_start_no + 2];
if (translate_dist == max_translate_dist)
{
// biggest possible change
break;
}
translate_dist += delta_dist;
}
else
{
translate_dist -= delta_dist;
}
}
else
// moved a little bit to far at the first time
// because of numerical inaccuracy
{
double shorten_value = sign * 0.5;
max_translate_dist -= shorten_value;
translate_dist -= shorten_value;
delta_dist -= shorten_value;
}
}
if (result == null)
{
return null;
}
if (board.changed_area != null)
{
FloatPoint new_prev_corner =
curr_lines[p_start_no].intersection_approx( curr_lines[p_start_no + 1]);
FloatPoint new_next_corner =
curr_lines[p_start_no + 3].intersection_approx( curr_lines[p_start_no + 4]);
board.changed_area.join(new_prev_corner, curr_layer);
board.changed_area.join(new_next_corner, curr_layer);
}
return result;
}
private Polyline skip_lines(Polyline p_polyline)
{
for (int i = 1; i < p_polyline.arr.length - 3; ++i)
{
for (int j = 0; j <= 1; ++j)
{
FloatPoint corner1;
FloatPoint corner2;
Line curr_line;
if (j == 0) // try to skip the line before the i+2-th line
{
curr_line = p_polyline.arr[i + 2];
corner1 = p_polyline.corner_approx(i);
corner2 = p_polyline.corner_approx(i - 1);
}
else // try to skip the line after i-th line
{
curr_line = p_polyline.arr[i];
corner1 = p_polyline.corner_approx(i + 1);
corner2 = p_polyline.corner_approx(i + 2);
}
boolean in_clip_shape = curr_clip_shape == null ||
curr_clip_shape.contains(corner1) &&
curr_clip_shape.contains(corner2);
if (!in_clip_shape)
{
continue;
}
Side side1 = curr_line.side_of(corner1);
Side side2 = curr_line.side_of(corner2);
if (side1 != side2)
// the two corners are on different sides of the line
{
Polyline reduced_polyline = p_polyline.skip_lines(i + 1, i + 1);
if (reduced_polyline.arr.length == p_polyline.arr.length - 1)
{
int shape_no = i - 1;
if (j == 0)
{
++shape_no;
}
TileShape shape_to_check =
reduced_polyline.offset_shape(curr_half_width, shape_no);
if (board.check_trace_shape(shape_to_check,
curr_layer, curr_net_no_arr, curr_cl_type, this.contact_pins))
{
if (board.changed_area != null)
{
board.changed_area.join(corner1, curr_layer);
board.changed_area.join(corner2, curr_layer);
}
return reduced_polyline;
}
}
}
// now try skipping 2 lines
if (i >= p_polyline.arr.length - 4)
{
break;
}
FloatPoint corner3;
if (j == 1)
{
corner3 = p_polyline.corner_approx(i + 3);
}
else
{
corner3 = p_polyline.corner_approx(i + 1);
}
if (curr_clip_shape != null && !curr_clip_shape.contains(corner3))
{
continue;
}
if (j == 0)
// curr_line is 1 line later than in the case skipping 1 line
// when coming from behind
{
curr_line = p_polyline.arr[i + 3];
side1 = curr_line.side_of(corner1);
side2 = curr_line.side_of(corner2);
}
else
{
side1 = curr_line.side_of(corner3);
}
if (side1 != side2)
// the two corners are on different sides of the line
{
Polyline reduced_polyline = p_polyline.skip_lines(i + 1, i + 2);
if (reduced_polyline.arr.length == p_polyline.arr.length - 2)
{
int shape_no = i - 1;
if (j == 0)
{
++shape_no;
}
TileShape shape_to_check =
reduced_polyline.offset_shape(curr_half_width, shape_no);
if (board.check_trace_shape(shape_to_check,
curr_layer, curr_net_no_arr, curr_cl_type, this.contact_pins))
{
if (board.changed_area != null)
{
board.changed_area.join(corner1, curr_layer);
board.changed_area.join(corner2, curr_layer);
board.changed_area.join(corner3, curr_layer);
}
return reduced_polyline;
}
}
}
}
}
return p_polyline;
}
Polyline smoothen_start_corner_at_trace(PolylineTrace p_trace)
{
boolean acute_angle = false;
boolean bend = false;
FloatPoint other_trace_corner_approx = null;
Line other_trace_line = null;
Line other_prev_trace_line = null;
Polyline trace_polyline = p_trace.polyline();
Point curr_end_corner = trace_polyline.corner(0);
if (this.curr_clip_shape != null && this.curr_clip_shape.is_outside(curr_end_corner))
{
return null;
}
Point curr_prev_end_corner = trace_polyline.corner(1);
boolean skip_short_segment = !(curr_end_corner instanceof IntPoint) &&
curr_end_corner.to_float().distance_square(curr_prev_end_corner.to_float()) < SKIP_LENGTH;
int start_line_no = 1;
if (skip_short_segment)
{
if (trace_polyline.corner_count() < 3)
{
return null;
}
curr_prev_end_corner = trace_polyline.corner(2);
++start_line_no;
}
Side prev_corner_side = null;
Direction line_direction = trace_polyline.arr[start_line_no].direction();
Direction prev_line_direction = trace_polyline.arr[start_line_no + 1].direction();
java.util.Collection<Item> contact_list = p_trace.get_start_contacts();
for (Item curr_contact : contact_list)
{
if (curr_contact instanceof PolylineTrace && !curr_contact.is_shove_fixed())
{
Polyline contact_trace_polyline = ((PolylineTrace) curr_contact).polyline();
FloatPoint curr_other_trace_corner_approx;
Line curr_other_trace_line;
Line curr_other_prev_trace_line;
if (contact_trace_polyline.first_corner().equals(curr_end_corner))
{
curr_other_trace_corner_approx = contact_trace_polyline.corner_approx(1);
curr_other_trace_line = contact_trace_polyline.arr[1];
curr_other_prev_trace_line = contact_trace_polyline.arr[2];
}
else
{
int curr_corner_no = contact_trace_polyline.corner_count() - 2;
curr_other_trace_corner_approx = contact_trace_polyline.corner_approx(curr_corner_no);
curr_other_trace_line = contact_trace_polyline.arr[curr_corner_no + 1].opposite();
curr_other_prev_trace_line = contact_trace_polyline.arr[curr_corner_no];
}
Side curr_prev_corner_side = curr_prev_end_corner.side_of(curr_other_trace_line);
Signum curr_projection = line_direction.projection(curr_other_trace_line.direction());
boolean other_trace_found = false;
if (curr_projection == Signum.POSITIVE && curr_prev_corner_side != Side.COLLINEAR)
{
acute_angle = true;
other_trace_found = true;
}
else if (curr_projection == Signum.ZERO && trace_polyline.corner_count() > 2)
{
if (prev_line_direction.projection(curr_other_trace_line.direction()) == Signum.POSITIVE)
{
bend = true;
other_trace_found = true;
}
}
if (other_trace_found)
{
other_trace_corner_approx = curr_other_trace_corner_approx;
other_trace_line = curr_other_trace_line;
prev_corner_side = curr_prev_corner_side;
other_prev_trace_line = curr_other_prev_trace_line;
}
}
else
{
return null;
}
}
int new_line_count = trace_polyline.arr.length + 1;
int diff = 1;
if (skip_short_segment)
{
--new_line_count;
--diff;
}
if (acute_angle)
{
Direction new_line_dir;
if (prev_corner_side == Side.ON_THE_LEFT)
{
new_line_dir = other_trace_line.direction().turn_45_degree(2);
}
else
{
new_line_dir = other_trace_line.direction().turn_45_degree(6);
}
Line translate_line = Line.get_instance(curr_end_corner.to_float().round(), new_line_dir);
double translate_dist = (Limits.sqrt2 - 1) * this.curr_half_width;
double prev_corner_dist = Math.abs(translate_line.signed_distance(curr_prev_end_corner.to_float()));
double other_dist = Math.abs(translate_line.signed_distance(other_trace_corner_approx));
translate_dist = Math.min(translate_dist, prev_corner_dist);
translate_dist = Math.min(translate_dist, other_dist);
if (translate_dist >= 0.99)
{
translate_dist = Math.max(translate_dist - 1, 1);
if (translate_line.side_of(curr_prev_end_corner) == Side.ON_THE_LEFT)
{
translate_dist = -translate_dist;
}
Line add_line = translate_line.translate(translate_dist);
// constract the new trace polyline.
Line[] new_lines = new Line[new_line_count];
new_lines[0] = other_trace_line;
new_lines[1] = add_line;
for (int i = 2; i < new_lines.length; ++i)
{
new_lines[i] = trace_polyline.arr[i - diff];
}
return new Polyline(new_lines);
}
}
else if (bend)
{
Line[] check_line_arr = new Line[new_line_count];
check_line_arr [0] = other_prev_trace_line;
check_line_arr [1] = other_trace_line;
for (int i = 2; i < check_line_arr.length; ++i)
{
check_line_arr [i] = trace_polyline.arr[i - diff];
}
Line new_line = reposition_line(check_line_arr, 0);
if (new_line != null)
{
Line [] new_lines = new Line[trace_polyline.arr.length];
new_lines[0] = other_trace_line;
new_lines[1] = new_line;
for (int i = 2; i < new_lines.length; ++i)
{
new_lines [i] = trace_polyline.arr[i];
}
return new Polyline(new_lines);
}
}
return null;
}
Polyline smoothen_end_corner_at_trace(PolylineTrace p_trace)
{
boolean acute_angle = false;
boolean bend = false;
FloatPoint other_trace_corner_approx = null;
Line other_trace_line = null;
Line other_prev_trace_line = null;
Polyline trace_polyline = p_trace.polyline();
Point curr_end_corner = trace_polyline.last_corner();
if (this.curr_clip_shape != null && this.curr_clip_shape.is_outside(curr_end_corner))
{
return null;
}
Point curr_prev_end_corner = trace_polyline.corner(trace_polyline.corner_count() - 2);
boolean skip_short_segment = !(curr_end_corner instanceof IntPoint) &&
curr_end_corner.to_float().distance_square(curr_prev_end_corner.to_float()) < SKIP_LENGTH;
int end_line_no = trace_polyline.arr.length - 2;
if (skip_short_segment)
{
if (trace_polyline.corner_count() < 3)
{
return null;
}
curr_prev_end_corner = trace_polyline.corner(trace_polyline.corner_count() - 3);
--end_line_no;
}
Side prev_corner_side = null;
Direction line_direction = trace_polyline.arr[end_line_no].direction().opposite();
Direction prev_line_direction = trace_polyline.arr[end_line_no].direction().opposite();
java.util.Collection<Item> contact_list = p_trace.get_end_contacts();
for (Item curr_contact : contact_list)
{
if (curr_contact instanceof PolylineTrace && !curr_contact.is_shove_fixed())
{
Polyline contact_trace_polyline = ((PolylineTrace) curr_contact).polyline();
if (contact_trace_polyline.corner_count() > 2)
{
FloatPoint curr_other_trace_corner_approx;
Line curr_other_trace_line;
Line curr_other_prev_trace_line;
if (contact_trace_polyline.first_corner().equals(curr_end_corner))
{
curr_other_trace_corner_approx = contact_trace_polyline.corner_approx(1);
curr_other_trace_line = contact_trace_polyline.arr[1];
curr_other_prev_trace_line = contact_trace_polyline.arr[2];
}
else
{
int curr_corner_no = contact_trace_polyline.corner_count() - 2;
curr_other_trace_corner_approx = contact_trace_polyline.corner_approx(curr_corner_no);
curr_other_trace_line = contact_trace_polyline.arr[curr_corner_no + 1].opposite();
curr_other_prev_trace_line = contact_trace_polyline.arr[curr_corner_no];
}
Side curr_prev_corner_side = curr_prev_end_corner.side_of(curr_other_trace_line);
Signum curr_projection = line_direction.projection(curr_other_trace_line.direction());
boolean other_trace_found = false;
if (curr_projection == Signum.POSITIVE && curr_prev_corner_side != Side.COLLINEAR)
{
acute_angle = true;
other_trace_found = true;
}
else if (curr_projection == Signum.ZERO && trace_polyline.corner_count() > 2)
{
if (prev_line_direction.projection(curr_other_trace_line.direction()) == Signum.POSITIVE)
{
bend = true;
other_trace_found = true;
}
}
if (other_trace_found)
{
other_trace_corner_approx = curr_other_trace_corner_approx;
other_trace_line = curr_other_trace_line;
prev_corner_side = curr_prev_corner_side;
other_prev_trace_line = curr_other_prev_trace_line;
}
}
}
else
{
return null;
}
}
int new_line_count = trace_polyline.arr.length + 1;
int diff = 0;
if (skip_short_segment)
{
--new_line_count;
++diff;
}
if (acute_angle)
{
Direction new_line_dir;
if (prev_corner_side == Side.ON_THE_LEFT)
{
new_line_dir = other_trace_line.direction().turn_45_degree(6);
}
else
{
new_line_dir = other_trace_line.direction().turn_45_degree(2);
}
Line translate_line = Line.get_instance(curr_end_corner.to_float().round(), new_line_dir);
double translate_dist = (Limits.sqrt2 - 1) * this.curr_half_width;
double prev_corner_dist = Math.abs(translate_line.signed_distance(curr_prev_end_corner.to_float()));
double other_dist = Math.abs(translate_line.signed_distance(other_trace_corner_approx));
translate_dist = Math.min(translate_dist, prev_corner_dist);
translate_dist = Math.min(translate_dist, other_dist);
if (translate_dist >= 0.99)
{
translate_dist = Math.max(translate_dist - 1, 1);
if (translate_line.side_of(curr_prev_end_corner) == Side.ON_THE_LEFT)
{
translate_dist = -translate_dist;
}
Line add_line = translate_line.translate(translate_dist);
// constract the new trace polyline.
Line[] new_lines = new Line[new_line_count];
for (int i = 0; i < trace_polyline.arr.length - 1; ++i)
{
new_lines[i] = trace_polyline.arr[i];
}
new_lines[new_lines.length - 2] = add_line;
new_lines[new_lines.length - 1] = other_trace_line;
return new Polyline(new_lines);
}
}
else if (bend)
{
Line[] check_line_arr = new Line[new_line_count];
for (int i = 0; i < check_line_arr.length - 2; ++i)
{
check_line_arr[i] = trace_polyline.arr[i + diff];
}
check_line_arr[check_line_arr.length - 2] = other_trace_line;
check_line_arr[check_line_arr.length - 1] = other_prev_trace_line;
Line new_line = reposition_line(check_line_arr, check_line_arr.length - 5);
if (new_line != null)
{
Line [] new_lines = new Line[trace_polyline.arr.length];
for (int i = 0; i < new_lines.length - 2; ++i)
{
new_lines[i] = trace_polyline.arr[i];
}
new_lines[new_lines.length - 2] = new_line;
new_lines[new_lines.length - 1] = other_trace_line;
return new Polyline(new_lines);
}
}
return null;
}
private static double SKIP_LENGTH = 10.0;
}