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

838 lines
34 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.
*
* PullTight90.java
*
* Created on 19. Juli 2003, 18:59
*/
package eu.mihosoft.freerouting.board;
import eu.mihosoft.freerouting.datastructures.Stoppable;
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.Limits;
import eu.mihosoft.freerouting.geometry.planar.Line;
import eu.mihosoft.freerouting.geometry.planar.Point;
import eu.mihosoft.freerouting.geometry.planar.Polyline;
import eu.mihosoft.freerouting.geometry.planar.Side;
import eu.mihosoft.freerouting.geometry.planar.TileShape;
import eu.mihosoft.freerouting.geometry.planar.Vector;
import eu.mihosoft.freerouting.datastructures.Signum;
/**
*
* @author Alfons Wirtz
*/
class PullTightAlgo45 extends PullTightAlgo
{
/** Creates a new instance of PullTight90 */
public PullTightAlgo45(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 (this.is_stop_requested())
{
break;
}
prev_result = new_result;
Polyline tmp1 = reduce_corners(prev_result);
Polyline tmp2 = smoothen_corners(tmp1);
new_result = reposition_lines(tmp2);
}
return new_result;
}
AngleRestriction get_angle_restriction()
{
return AngleRestriction.FORTYFIVE_DEGREE;
}
/**
* Tries to reduce the amount of corners of p_polyline.
* Return p_polyline, if nothing was changed.
*/
private Polyline reduce_corners(Polyline p_polyline)
{
if (p_polyline.arr.length <= 4)
{
return p_polyline;
}
int new_corner_count = 1;
Point[] curr_corner = new Point[4];
for (int i = 0; i < 4; ++i)
{
curr_corner[i] = p_polyline.corner(i);
if (!(curr_corner[i] instanceof IntPoint))
{
return p_polyline;
}
}
boolean[] curr_corner_in_clip_shape = new boolean[4];
for (int i = 0; i < 4; ++i)
{
if (curr_clip_shape == null)
{
curr_corner_in_clip_shape[i] = true;
}
else
{
curr_corner_in_clip_shape[i] = !curr_clip_shape.is_outside(curr_corner[i]);
}
}
boolean polyline_changed = false;
Point[] new_corners = new Point[p_polyline.arr.length - 3];
new_corners[0] = curr_corner[0];
Point[] curr_check_points = new Point[2];
Point new_corner = null;
int corner_no = 3;
while (corner_no < p_polyline.arr.length - 1)
{
boolean corner_removed = false;
curr_corner[3] = p_polyline.corner(corner_no);
if (!(curr_corner[3] instanceof IntPoint))
{
return p_polyline;
}
if (curr_corner[1].equals(curr_corner[2]) ||
corner_no < p_polyline.arr.length - 2 &&
curr_corner[3].side_of(curr_corner[1], curr_corner[2]) == Side.COLLINEAR)
{
// corners in the middle af a line can be skipped
++corner_no;
curr_corner[2] = curr_corner[3];
curr_corner_in_clip_shape[2] = curr_corner_in_clip_shape[3];
if (corner_no < p_polyline.arr.length - 1)
{
curr_corner[3] = p_polyline.corner(corner_no);
if (!(curr_corner[3] instanceof IntPoint))
{
return p_polyline;
}
}
polyline_changed = true;
}
curr_corner_in_clip_shape[3] = curr_clip_shape == null || !curr_clip_shape.is_outside(curr_corner[3]);
if (curr_corner_in_clip_shape[1] && curr_corner_in_clip_shape[2] && curr_corner_in_clip_shape[3])
{
// translate the line from curr_corner[2] to curr_corner[1] to curr_corner[3]
Vector delta = curr_corner[3].difference_by(curr_corner[2]);
new_corner = curr_corner[1].translate_by(delta);
if (curr_corner[3].equals(curr_corner[2]))
{
// just remove multiple corner
corner_removed = true;
}
else if (new_corner.side_of(curr_corner[0], curr_corner[1]) == Side.COLLINEAR)
{
curr_check_points[0] = new_corner;
curr_check_points[1] = curr_corner[1];
Polyline check_polyline = new Polyline(curr_check_points);
if (check_polyline.arr.length == 3)
{
TileShape shape_to_check = check_polyline.offset_shape(curr_half_width, 0);
if (board.check_trace_shape(shape_to_check, curr_layer, curr_net_no_arr,
curr_cl_type, this.contact_pins))
{
curr_check_points[1] = curr_corner[3];
if (curr_check_points[0].equals(curr_check_points[1]))
{
corner_removed = true;
}
else
{
check_polyline = new Polyline(curr_check_points);
if (check_polyline.arr.length == 3)
{
shape_to_check = check_polyline.offset_shape(curr_half_width, 0);
corner_removed = board.check_trace_shape(shape_to_check, curr_layer, curr_net_no_arr,
curr_cl_type, this.contact_pins);
}
else
{
corner_removed = true;
}
}
}
}
else
{
corner_removed = true;
}
}
}
if (!corner_removed && curr_corner_in_clip_shape[0] && curr_corner_in_clip_shape[1] && curr_corner_in_clip_shape[2])
{
// the first try has failed. Try to translate the line from
// corner_2 to corner_1 to corner_0
Vector delta = curr_corner[0].difference_by(curr_corner[1]);
new_corner = curr_corner[2].translate_by(delta);
if (curr_corner[0].equals(curr_corner[1]))
{
// just remove multiple corner
corner_removed = true;
}
else if (new_corner.side_of(curr_corner[2], curr_corner[3]) == Side.COLLINEAR)
{
curr_check_points[0] = new_corner;
curr_check_points[1] = curr_corner[0];
Polyline check_polyline = new Polyline(curr_check_points);
if (check_polyline.arr.length == 3)
{
TileShape shape_to_check = check_polyline.offset_shape(curr_half_width, 0);
if (board.check_trace_shape(shape_to_check, curr_layer, curr_net_no_arr,
curr_cl_type, this.contact_pins))
{
curr_check_points[1] = curr_corner[2];
check_polyline = new Polyline(curr_check_points);
if (check_polyline.arr.length == 3)
{
shape_to_check = check_polyline.offset_shape(curr_half_width, 0);
corner_removed = board.check_trace_shape(shape_to_check, curr_layer, curr_net_no_arr,
curr_cl_type, this.contact_pins);
}
else
{
corner_removed = true;
}
}
}
else
{
corner_removed = true;
}
}
}
if (corner_removed)
{
polyline_changed = true;
curr_corner[1] = new_corner;
curr_corner_in_clip_shape[1] =
curr_clip_shape == null || !curr_clip_shape.is_outside(curr_corner[1]);
if (board.changed_area != null)
{
board.changed_area.join(new_corner.to_float(), curr_layer);
board.changed_area.join(curr_corner[1].to_float(), curr_layer);
board.changed_area.join(curr_corner[2].to_float(), curr_layer);
}
}
else
{
new_corners[new_corner_count] = curr_corner[1];
++new_corner_count;
curr_corner[0] = curr_corner[1];
curr_corner[1] = curr_corner[2];
curr_corner_in_clip_shape[0] = curr_corner_in_clip_shape[1];
curr_corner_in_clip_shape[1] = curr_corner_in_clip_shape[2];
}
curr_corner[2] = curr_corner[3];
curr_corner_in_clip_shape[2] = curr_corner_in_clip_shape[3];
++corner_no;
}
if (!polyline_changed)
{
return p_polyline;
}
Point adjusted_corners[] = new Point[new_corner_count + 2];
for (int i = 0; i < new_corner_count; ++i)
{
adjusted_corners[i] = new_corners[i];
}
adjusted_corners[new_corner_count] = curr_corner[1];
adjusted_corners[new_corner_count + 1] = curr_corner[2];
Polyline result = new Polyline(adjusted_corners);
return result;
}
/**
* Smoothens the 90 degree corners of p_polyline to 45 degree
* by cutting of the 90 degree corner. The cutting of is so small,
* that no check is needed
*/
private Polyline smoothen_corners(Polyline p_polyline)
{
Polyline result = p_polyline;
boolean polyline_changed = true;
while (polyline_changed)
{
if (result.arr.length < 4)
{
return result;
}
polyline_changed = false;
Line[] line_arr = new Line[result.arr.length];
System.arraycopy(result.arr, 0, line_arr, 0, line_arr.length);
for (int i = 1; i < line_arr.length - 2; ++i)
{
Direction d1 = line_arr[i].direction();
Direction d2 = line_arr[i + 1].direction();
if (d1.is_multiple_of_45_degree() && d2.is_multiple_of_45_degree() && d1.projection(d2) != Signum.POSITIVE)
{
// there is a 90 degree or sharper angle
Line new_line = smoothen_corner(line_arr, i);
if (new_line == null)
{
//the greedy smoothening couldn't change the polyline
new_line = smoothen_sharp_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 + 1);
tmp_lines[i + 1] = new_line;
System.arraycopy(line_arr, i + 1, tmp_lines, i + 2,
tmp_lines.length - (i + 2));
line_arr = tmp_lines;
++i;
}
}
}
if (polyline_changed)
{
result = new Polyline(line_arr);
}
}
return result;
}
/**
* adds a line between at p_no to smoothen a
* 90 degree corner between p_line_1 and p_line_2 to 45 degree.
* The distance of the new line to the corner will be so small
* that no clearance check is necessary.
*/
private Line smoothen_sharp_corner(Line[] p_line_arr, int p_no)
{
FloatPoint curr_corner = p_line_arr[p_no].intersection_approx(p_line_arr[p_no + 1]);
if (curr_corner.x != (int) curr_corner.x)
{
// intersection of 2 diagonal lines is not integer
Line result = smoothen_non_integer_corner(p_line_arr, p_no);
{
if (result != null)
{
return result;
}
}
}
FloatPoint prev_corner = p_line_arr[p_no].intersection_approx(p_line_arr[p_no - 1]);
FloatPoint next_corner = p_line_arr[p_no + 1].intersection_approx(p_line_arr[p_no + 2]);
Direction prev_dir = p_line_arr[p_no].direction();
Direction next_dir = p_line_arr[p_no + 1].direction();
Direction new_line_dir = Direction.get_instance(prev_dir.get_vector().add(next_dir.get_vector()));
Line translate_line = Line.get_instance(curr_corner.round(), new_line_dir);
double translate_dist = (Limits.sqrt2 - 1) * this.curr_half_width;
double prev_dist = Math.abs(translate_line.signed_distance(prev_corner));
double next_dist = Math.abs(translate_line.signed_distance(next_corner));
translate_dist = Math.min(translate_dist, prev_dist);
translate_dist = Math.min(translate_dist, next_dist);
if (translate_dist < 0.99)
{
return null;
}
translate_dist = Math.max(translate_dist - 1, 1);
if (translate_line.side_of(next_corner) == Side.ON_THE_LEFT)
{
translate_dist = -translate_dist;
}
Line result = translate_line.translate(translate_dist);
if (board.changed_area != null)
{
board.changed_area.join(curr_corner, curr_layer);
}
return result;
}
/**
* Smoothens with a short axis parrallel line to remove a non integer corner
* of two intersecting diagonal lines.
* Returns null, if that is not possible.
*/
private Line smoothen_non_integer_corner(Line[] p_line_arr, int p_no)
{
Line prev_line = p_line_arr[p_no];
Line next_line = p_line_arr[p_no + 1];
if (prev_line.is_equal_or_opposite(next_line))
{
return null;
}
if (!(prev_line.is_diagonal() && next_line.is_diagonal()))
{
return null;
}
FloatPoint curr_corner = prev_line.intersection_approx(next_line);
FloatPoint prev_corner = prev_line.intersection_approx(p_line_arr[p_no - 1]);
FloatPoint next_corner = next_line.intersection_approx(p_line_arr[p_no + 2]);
Line result = null;
int new_x = 0;
int new_y = 0;
boolean new_line_is_vertical = false;
boolean new_line_is_horizontal = false;
if (prev_corner.x > curr_corner.x && next_corner.x > curr_corner.x)
{
new_x = (int) Math.ceil(curr_corner.x);
new_y = (int) Math.ceil(curr_corner.y);
new_line_is_vertical = true;
}
else if (prev_corner.x < curr_corner.x && next_corner.x < curr_corner.x)
{
new_x = (int) Math.floor(curr_corner.x);
new_y = (int) Math.floor(curr_corner.y);
new_line_is_vertical = true;
}
else if (prev_corner.y > curr_corner.y && next_corner.y > curr_corner.y)
{
new_x = (int) Math.ceil(curr_corner.x);
new_y = (int) Math.ceil(curr_corner.y);
new_line_is_horizontal = true;
}
else if (prev_corner.y < curr_corner.y && next_corner.y < curr_corner.y)
{
new_x = (int) Math.floor(curr_corner.x);
new_y = (int) Math.floor(curr_corner.y);
new_line_is_horizontal = true;
}
Direction new_line_dir = null;
if (new_line_is_vertical)
{
if (prev_corner.y < next_corner.y)
{
new_line_dir = Direction.UP;
}
else
{
new_line_dir = Direction.DOWN;
}
}
else if (new_line_is_horizontal)
{
if (prev_corner.x < next_corner.x)
{
new_line_dir = Direction.RIGHT;
}
else
{
new_line_dir = Direction.LEFT;
}
}
else
{
return null;
}
Point line_a = new IntPoint(new_x, new_y);
result = new Line(line_a, new_line_dir);
return result;
}
/**
* adds a line between at p_no to smoothen a
* 90 degree corner between p_line_1 and p_line_2 to 45 degree.
* The distance of the new line to the corner will be so big
* that a clearance check is necessary.
*/
private Line smoothen_corner(Line[] p_line_arr, int p_no)
{
FloatPoint prev_corner = p_line_arr[p_no].intersection_approx(p_line_arr[p_no - 1]);
FloatPoint curr_corner = p_line_arr[p_no].intersection_approx(p_line_arr[p_no + 1]);
FloatPoint next_corner = p_line_arr[p_no + 1].intersection_approx(p_line_arr[p_no + 2]);
Direction prev_dir = p_line_arr[p_no].direction();
Direction next_dir = p_line_arr[p_no + 1].direction();
Direction new_line_dir = Direction.get_instance(prev_dir.get_vector().add(next_dir.get_vector()));
Line translate_line = Line.get_instance(curr_corner.round(), new_line_dir);
double prev_dist = Math.abs(translate_line.signed_distance(prev_corner));
double next_dist = Math.abs(translate_line.signed_distance(next_corner));
if (prev_dist == 0 || next_dist == 0)
{
return null;
}
double max_translate_dist;
FloatPoint nearest_corner;
if (prev_dist <= next_dist)
{
max_translate_dist = prev_dist;
nearest_corner = prev_corner;
}
else
{
max_translate_dist = next_dist;
nearest_corner = next_corner;
}
if (max_translate_dist < 1)
{
return null;
}
max_translate_dist = Math.max(max_translate_dist - 1, 1);
if (translate_line.side_of(next_corner) == Side.ON_THE_LEFT)
{
max_translate_dist = -max_translate_dist;
}
Line[] check_lines = new Line[3];
check_lines[0] = p_line_arr[p_no];
check_lines[2] = p_line_arr[p_no + 1];
double translate_dist = max_translate_dist;
double delta_dist = max_translate_dist;
Side side_of_nearest_corner = translate_line.side_of(nearest_corner);
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_corner = new_line.side_of(nearest_corner);
if (new_line_side_of_nearest_corner == side_of_nearest_corner || new_line_side_of_nearest_corner == Side.COLLINEAR)
{
check_lines[1] = new_line;
Polyline tmp = new Polyline(check_lines);
if (tmp.arr.length == 3)
{
TileShape shape_to_check =
tmp.offset_shape(curr_half_width, 0);
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 = check_lines[1];
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 && board.changed_area != null)
{
FloatPoint new_prev_corner =
check_lines[0].intersection_approx(result);
FloatPoint new_next_corner =
check_lines[2].intersection_approx(result);
board.changed_area.join(new_prev_corner, curr_layer);
board.changed_area.join(new_next_corner, curr_layer);
board.changed_area.join(curr_corner, curr_layer);
}
return result;
}
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);
Side prev_corner_side = null;
Direction line_direction = trace_polyline.arr[1].direction();
Direction prev_line_direction = trace_polyline.arr[2].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)
{
if (curr_other_trace_line.direction().is_orthogonal())
{
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;
}
}
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[trace_polyline.arr.length + 1];
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 - 1];
}
return new Polyline(new_lines);
}
}
else if (bend)
{
Line[] check_line_arr = new Line[trace_polyline.arr.length + 1];
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 - 1];
}
Line new_line = reposition_line(check_line_arr, 2);
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);
Side prev_corner_side = null;
Direction line_direction = trace_polyline.arr[trace_polyline.arr.length - 2].direction().opposite();
Direction prev_line_direction = trace_polyline.arr[trace_polyline.arr.length - 3].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();
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)
{
if (curr_other_trace_line.direction().is_orthogonal())
{
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;
}
}
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[trace_polyline.arr.length + 1];
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[trace_polyline.arr.length + 1];
for (int i = 0; i < trace_polyline.arr.length - 1; ++i)
{
check_line_arr[i] = trace_polyline.arr[i];
}
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, trace_polyline.arr.length - 2);
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;
}
}