838 lines
34 KiB
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;
|
|
}
|
|
}
|