/*
* 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 geometry.planar;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
/**
*
* Convex shape defined as intersection of half-planes.
* A half-plane is defined as the positive side of a directed line.
*
* @author Alfons Wirtz
*/
public class Simplex extends TileShape implements java.io.Serializable
{
/**
* Standard implementation for an empty Simplex.
*/
public static final Simplex EMPTY = new Simplex(new Line [0]);
/**
* creates a Simplex as intersection of the halfplanes defined
* by an array of directed lines
*/
public static Simplex get_instance(Line[] p_line_arr)
{
if (p_line_arr.length <= 0)
{
return Simplex.EMPTY;
}
Line [] curr_arr = new Line[p_line_arr.length];
System.arraycopy(p_line_arr, 0, curr_arr, 0, p_line_arr.length);
// sort the lines in ascending direction
java.util.Arrays.sort(curr_arr);
Simplex curr_simplex = new Simplex(curr_arr);
Simplex result = curr_simplex.remove_redundant_lines();
return result;
}
/**
* Return true, if this simplex is empty
*/
public boolean is_empty()
{
return (arr.length == 0);
}
/**
* Converts the physical instance of this shape to a simpler physical instance, if possible.
* (For example a Simplex to an IntOctagon).
*/
public TileShape simplify()
{
TileShape result = this;
if (this.is_empty())
{
result = Simplex.EMPTY;
}
else if (this.is_IntBox())
{
result = this.bounding_box();
}
else if (this.is_IntOctagon())
{
result = this.to_IntOctagon();
}
return result;
}
/**
* Returns true, if the determinant of the direction of index
* p_no -1 and the direction of index p_no is > 0
*/
public boolean corner_is_bounded(int p_no)
{
int no;
if (p_no < 0)
{
System.out.println("corner: p_no is < 0");
no = 0;
}
else if (p_no >= arr.length)
{
System.out.println("corner: p_index must be less than arr.length - 1");
no = arr.length - 1;
}
else
{
no = p_no;
}
if(arr.length == 1)
{
return false;
}
int prev_no;
if (no == 0)
{
prev_no = arr.length - 1;
}
else
{
prev_no = no - 1;
}
IntVector prev_dir = (IntVector)arr[prev_no].direction().get_vector();
IntVector curr_dir = (IntVector)arr[no].direction().get_vector();
return (prev_dir.determinant(curr_dir) > 0);
}
/**
* Returns true, if the shape of this simplex is contained in a
* sufficiently large box
*/
public boolean is_bounded()
{
if (arr.length == 0)
{
return true;
}
if (arr.length < 3)
{
return false;
}
for (int i = 0; i < arr.length; ++i)
{
if (!corner_is_bounded(i))
{
return false;
}
}
return true;
}
/**
* Returns the number of edge lines defining this simplex
*/
public int border_line_count()
{
return arr.length;
}
/**
* Returns the intersection of the p_no -1-th with the p_no-th line of this simplex.
* If the simplex is not bounded at this corner, the
* coordinates of the result will be set to Integer.MAX_VALUE.
*/
public Point corner(int p_no)
{
int no;
if (p_no < 0)
{
System.out.println("Simplex.corner: p_no is < 0");
no = 0;
}
else if (p_no >= arr.length)
{
System.out.println("Simplex.corner: p_no must be less than arr.length - 1");
no = arr.length - 1;
}
else
{
no = p_no;
}
if (precalculated_corners == null)
// corner array is not yet allocated
{
precalculated_corners = new Point[arr.length];
}
if (precalculated_corners [no] == null)
// corner is not yet calculated
{
Line prev;
if (no == 0)
{
prev = arr[arr.length - 1];
}
else
{
prev = arr[no - 1];
}
precalculated_corners[no] = arr[no].intersection(prev);
}
return precalculated_corners [no];
}
/**
* Returns an approximation of the intersection of the p_no -1-th with the
* p_no-th line of this simplex by a FloatPoint.
* If the simplex is not bounded at this corner, the
* coordinates of the result will be set to Integer.MAX_VALUE.
*/
public FloatPoint corner_approx(int p_no)
{
if (arr.length <= 0)
{
return null;
}
int no;
if (p_no < 0)
{
System.out.println("Simplex.corner_approx: p_no is < 0");
no = 0;
}
else if (p_no >= arr.length)
{
System.out.println("Simplex.corner_approx: p_no must be less than arr.length - 1");
no = arr.length - 1;
}
else
{
no = p_no;
}
if (precalculated_float_corners == null)
// corner array is not yet allocated
{
precalculated_float_corners = new FloatPoint[arr.length];
}
if (precalculated_float_corners [no] == null)
// corner is not yet calculated
{
Line prev;
if (no == 0)
{
prev = arr[arr.length - 1];
}
else
{
prev = arr[no - 1];
}
precalculated_float_corners[no] = arr[no].intersection_approx(prev);
}
return precalculated_float_corners [no];
}
public FloatPoint[] corner_approx_arr()
{
if (precalculated_float_corners == null)
// corner array is not yet allocated
{
precalculated_float_corners = new FloatPoint[arr.length];
}
for (int i = 0; i < precalculated_float_corners.length; ++i)
{
if (precalculated_float_corners [i] == null)
// corner is not yet calculated
{
Line prev;
if (i == 0)
{
prev = arr[arr.length - 1];
}
else
{
prev = arr[i - 1];
}
precalculated_float_corners[i] = arr[i].intersection_approx(prev);
}
}
return precalculated_float_corners;
}
/**
* returns the p_no-th edge line of this simplex.
* The edge lines are sorted in ascending direction.
*/
public Line border_line(int p_no)
{
if (arr.length <= 0)
{
System.out.println("Simplex.edge_line : simplex is empty");
return null;
}
int no;
if (p_no < 0)
{
System.out.println("Simplex.edge_line : p_no is < 0");
no = 0;
}
else if (p_no >= arr.length)
{
System.out.println("Simplex.edge_line: p_no must be less than arr.length - 1");
no = arr.length - 1;
}
else
{
no = p_no;
}
return arr[no];
}
/**
* Returns the dimension of this simplex.
* The result may be 2, 1, 0, or -1 (if the simplex is empty).
*/
public int dimension()
{
if (arr.length == 0)
{
return -1;
}
if (arr.length > 4)
{
return 2;
}
if (arr.length == 1)
{
// we have a half plane
return 2;
}
if (arr.length == 2)
{
if(arr[0].overlaps(arr[1]))
{
return 1;
}
return 2;
}
if (arr.length == 3)
{
if (arr[0].overlaps(arr[1]) || arr[0].overlaps(arr[2])
|| arr[1].overlaps(arr[2]))
{
// simplex is 1 dimensional and unbounded at one side
return 1;
}
Point intersection = arr[1].intersection(arr[2]);
Side side_of_line0 = arr[0].side_of(intersection);
if(side_of_line0 == Side.ON_THE_RIGHT)
{
return 2;
}
if (side_of_line0 == Side.ON_THE_LEFT)
{
System.out.println("empty Simplex not normalized");
return -1;
}
// now the 3 lines intersect in the same point
return 0;
}
// now the simplex has 4 edge lines
// check if opposing lines are collinear
boolean collinear_0_2 = arr[0].overlaps(arr[2]);
boolean collinear_1_3 = arr[1].overlaps(arr[3]);
if (collinear_0_2 && collinear_1_3)
{
return 0;
}
if (collinear_0_2 || collinear_1_3)
{
return 1;
}
return 2;
}
public double max_width()
{
if (!this.is_bounded())
{
return Integer.MAX_VALUE;
}
double max_distance = Integer.MIN_VALUE;
double max_distance_2 = Integer.MIN_VALUE;
FloatPoint gravity_point = this.centre_of_gravity();
for (int i = 0; i < border_line_count(); ++i)
{
double curr_distance = Math.abs(arr[i].signed_distance(gravity_point));
if (curr_distance > max_distance)
{
max_distance_2 = max_distance;
max_distance = curr_distance;
}
else if (curr_distance > max_distance_2)
{
max_distance_2 = curr_distance;
}
}
return max_distance + max_distance_2;
}
public double min_width()
{
if (!this.is_bounded())
{
return Integer.MAX_VALUE;
}
double min_distance = Integer.MAX_VALUE;
double min_distance_2 = Integer.MAX_VALUE;
FloatPoint gravity_point = this.centre_of_gravity();
for (int i = 0; i < border_line_count(); ++i)
{
double curr_distance = Math.abs(arr[i].signed_distance(gravity_point));
if (curr_distance < min_distance)
{
min_distance_2 = min_distance;
min_distance = curr_distance;
}
else if (curr_distance < min_distance_2)
{
min_distance_2 = curr_distance;
}
}
return min_distance + min_distance_2;
}
/**
* checks if this simplex can be converted into an IntBox
*/
public boolean is_IntBox()
{
for (int i = 0; i < arr.length; ++i)
{
Line curr_line = arr[i];
if (!(curr_line.a instanceof IntPoint &&
curr_line.b instanceof IntPoint ))
{
return false;
}
if (!curr_line.is_orthogonal())
{
return false;
}
if (!corner_is_bounded(i))
{
return false;
}
}
return true;
}
/**
* checks if this simplex can be converted into an IntOctagon
*/
public boolean is_IntOctagon()
{
for (int i = 0; i < arr.length; ++i)
{
Line curr_line = arr[i];
if (!(curr_line.a instanceof IntPoint &&
curr_line.b instanceof IntPoint ))
{
return false;
}
if (!curr_line.is_multiple_of_45_degree())
{
return false;
}
if (!corner_is_bounded(i))
{
return false;
}
}
return true;
}
/**
* Converts this IntSimplex to an IntOctagon.
* Returns null, if that is not possible, because not all lines
* of this IntSimplex are 45 degree
*/
public IntOctagon to_IntOctagon()
{
// this function is at the moment only implemented for lines
// consisting of IntPoints.
// The general implementation is still missing.
if (!is_IntOctagon())
{
return null;
}
if (is_empty())
{
return IntOctagon.EMPTY;
}
// initialise to biggest octagon values
int rx = Limits.CRIT_INT;
int uy = Limits.CRIT_INT;
int lrx = Limits.CRIT_INT;
int urx = Limits.CRIT_INT;
int lx = -Limits.CRIT_INT;
int ly = -Limits.CRIT_INT;
int llx = -Limits.CRIT_INT;
int ulx = -Limits.CRIT_INT;
for (int i = 0; i < arr.length; ++i)
{
Line curr_line = arr[i];
IntPoint a = (IntPoint) curr_line.a;
IntPoint b = (IntPoint) curr_line.b;
if (a.y == b.y)
{
if (b.x >= a.x)
{
// lower boundary line
ly = a.y;
}
if (b.x <= a.x)
{
// upper boundary line
uy = a.y;
}
}
if (a.x == b.x)
{
if (b.y >= a.y)
{
// right boundary line
rx = a.x;
}
if (b.y <= a.y)
{
// left boundary line
lx = a.x;
}
}
if (a.y < b.y)
{
if (a.x < b.x)
{
// lower right boundary line
lrx = a.x - a.y;
}
else if (a.x > b.x)
{
// upper right boundary line
urx = a.x + a.y;
}
}
else if (a.y > b.y)
{
if (a.x < b.x)
{
// lower left boundary line
llx = a.x + a.y;
}
else if (a.x > b.x)
{
// upper left boundary line
ulx = a.x - a.y;
}
}
}
IntOctagon result = new IntOctagon(lx, ly, rx, uy, ulx, lrx, llx, urx);
return result.normalize();
}
/**
* Returns the simplex, which results from translating
* the lines of this simplex by p_vector
*/
public Simplex translate_by(Vector p_vector)
{
if (p_vector.equals(Vector.ZERO))
{
return this;
}
Line[] new_arr = new Line[arr.length];
for( int i = 0; i < arr.length; ++i)
{
new_arr [i] = arr[i].translate_by(p_vector);
}
return new Simplex(new_arr);
}
/**
* Returns the smallest box with int coordinates containing
* all corners of this simplex.
* The coordinates of the result will be Integer.MAX_VALUE,
* if the simplex is not bounded
*/
public IntBox bounding_box()
{
if (arr.length == 0)
{
return IntBox.EMPTY;
}
if (precalculated_bounding_box == null)
{
double llx = Integer.MAX_VALUE;
double lly = Integer.MAX_VALUE;
double urx = Integer.MIN_VALUE;
double ury = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; ++i)
{
FloatPoint curr = corner_approx(i);
llx = Math.min(llx, curr.x);
lly = Math.min(lly, curr.y);
urx = Math.max(urx, curr.x);
ury = Math.max(ury, curr.y);
}
IntPoint lower_left = new IntPoint((int)Math.floor(llx),(int)Math.floor(lly));
IntPoint upper_right = new IntPoint((int)Math.ceil(urx),(int)Math.ceil(ury));
precalculated_bounding_box = new IntBox(lower_left, upper_right);
}
return precalculated_bounding_box;
}
/**
* Calculates a bounding octagon of the Simplex.
* Returns null, if the Simplex is not bounded.
*/
public IntOctagon bounding_octagon()
{
if (precalculated_bounding_octagon == null)
{
double lx = Integer.MAX_VALUE;
double ly = Integer.MAX_VALUE;
double rx = Integer.MIN_VALUE;
double uy = Integer.MIN_VALUE;
double ulx = Integer.MAX_VALUE;
double lrx = Integer.MIN_VALUE;
double llx = Integer.MAX_VALUE;
double urx = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; ++i)
{
FloatPoint curr = corner_approx(i);
lx = Math.min(lx, curr.x);
ly = Math.min(ly, curr.y);
rx = Math.max(rx, curr.x);
uy = Math.max(uy, curr.y);
double tmp = curr.x - curr.y;
ulx = Math.min(ulx, tmp);
lrx = Math.max(lrx, tmp);
tmp = curr.x + curr.y;
llx = Math.min(llx, tmp);
urx = Math.max(urx, tmp);
}
if (Math.min(lx, ly) < -Limits.CRIT_INT
|| Math.max(rx, uy) > Limits.CRIT_INT
|| Math.min(ulx, llx) < -Limits.CRIT_INT
|| Math.max(lrx, urx) > Limits.CRIT_INT)
// result is not bounded
{
return null;
}
precalculated_bounding_octagon = new
IntOctagon((int)Math.floor(lx), (int)Math.floor(ly),
(int)Math.ceil(rx), (int)Math.ceil(uy),
(int)Math.floor(ulx), (int)Math.ceil(lrx),
(int)Math.floor(llx), (int)Math.ceil(urx));
}
return precalculated_bounding_octagon;
}
public Simplex bounding_tile()
{
return this;
}
public RegularTileShape bounding_shape(ShapeBoundingDirections p_dirs)
{
return p_dirs.bounds(this);
}
/**
* Returns the simplex offseted by p_with.
* If p_width > 0, the offset is to the outer, else to the inner.
*/
public Simplex offset(double p_width)
{
if (p_width == 0)
{
return this;
}
Line[] new_arr = new Line[arr.length];
for (int i = 0; i < arr.length; ++i)
{
new_arr[i] = arr[i].translate(-p_width);
}
Simplex offset_simplex = new Simplex(new_arr);
if (p_width < 0)
{
offset_simplex = offset_simplex.remove_redundant_lines();
}
return offset_simplex;
}
/**
* Returns this simplex enlarged by p_offset.
* The result simplex is intersected with the
* by p_offset enlarged bounding octagon of this simplex
*/
public Simplex enlarge(double p_offset)
{
if (p_offset == 0)
{
return this;
}
Simplex offset_simplex = offset(p_offset);
IntOctagon bounding_oct = this.bounding_octagon();
if (bounding_oct == null)
{
return Simplex.EMPTY;
}
IntOctagon offset_oct = bounding_oct.offset(p_offset);
return offset_simplex.intersection(offset_oct.to_Simplex());
}
/**
* Returns the number of the rightmost corner seen from p_from_point
* No other point of this simplex may be to the right
* of the line from p_from_point to the result corner.
*/
public int index_of_right_most_corner( Point p_from_point)
{
Point pole = p_from_point;
Point right_most_corner = corner(0);
int result = 0;
for (int i = 1; i < arr.length; ++i)
{
Point curr_corner = corner(i);
if (curr_corner.side_of(pole, right_most_corner) == Side.ON_THE_RIGHT)
{
right_most_corner = curr_corner;
result = i;
}
}
return result;
}
/**
* Returns the intersection of p_box with this simplex
*/
public Simplex intersection(IntBox p_box)
{
return intersection(p_box.to_Simplex());
}
/**
* Returns the intersection of this simplex and p_other
*/
public Simplex intersection(Simplex p_other)
{
if (this.is_empty() || p_other.is_empty())
{
return EMPTY;
}
Line[] new_arr = new Line[arr.length + p_other.arr.length];
System.arraycopy(arr, 0, new_arr, 0, arr.length);
System.arraycopy(p_other.arr, 0, new_arr, arr.length, p_other.arr.length);
java.util.Arrays.sort(new_arr);
Simplex result = new Simplex( new_arr);
return result.remove_redundant_lines();
}
/**
* Returns the intersection of this simplex and the shape p_other
*/
public TileShape intersection(TileShape p_other)
{
TileShape result = p_other.intersection(this);
return result;
}
public boolean intersects(Shape p_other)
{
return p_other.intersects(this);
}
public boolean intersects(Simplex p_other)
{
ConvexShape is = intersection(p_other);
return !is.is_empty();
}
/**
* if p_line is a borderline of this simplex the number of that
* edge is returned, otherwise -1
*/
public int border_line_index(Line p_line)
{
for (int i = 0; i < arr.length; ++i)
{
if (p_line.equals(arr[i]))
{
return i;
}
}
return -1;
}
/**
* Enlarges the simplex by removing the edge line with index p_no.
* The result simplex may get unbounded.
*/
public Simplex remove_border_line( int p_no)
{
if (p_no < 0 || p_no >= arr.length)
{
return this;
}
Line [] new_arr = new Line [this.arr.length - 1];
System.arraycopy(this.arr, 0, new_arr, 0, p_no);
System.arraycopy(this.arr, p_no + 1, new_arr, p_no, new_arr.length - p_no);
return new Simplex(new_arr);
}
/**
* Constructs a Simplex from the directed lines in p_line_arr.
* The simplex will not be normalized.
* To get a normalised simplex use TileShape.get_instance
*/
public Simplex(Line[] p_line_arr)
{
arr = p_line_arr;
}
public Simplex to_Simplex()
{
return this;
}
Simplex intersection(IntOctagon p_other)
{
return intersection(p_other.to_Simplex());
}
public TileShape[] cutout(TileShape p_shape)
{
return p_shape.cutout_from(this);
}
/**
* cuts this simplex out of p_outer_simplex.
* Divides the resulting shape into simplices along the minimal
* distance lines from the vertices of the inner simplex to the outer
* simplex; Returns the convex pieces constructed by this division.
*/
public Simplex[] cutout_from(Simplex p_outer_simplex)
{
if(this.dimension() < 2)
{
System.out.println("Simplex.cutout_from only implemented for 2-dim simplex");
return null;
}
Simplex inner_simplex = this.intersection(p_outer_simplex);
if (inner_simplex.dimension() < 2)
{
// nothing to cutout from p_outer_simplex
Simplex[] result = new Simplex[1];
result[0] = p_outer_simplex;
return result;
}
int inner_corner_count = inner_simplex.arr.length;
Line [][] division_line_arr = new Line[inner_corner_count][];
for (int inner_corner_no = 0; inner_corner_no < inner_corner_count; ++inner_corner_no)
{
division_line_arr[inner_corner_no] =
inner_simplex.calc_division_lines(inner_corner_no, p_outer_simplex);
if (division_line_arr[inner_corner_no] == null)
{
System.out.println("Simplex.cutout_from: division line is null");
Simplex[] result = new Simplex[1];
result[0] = p_outer_simplex;
return result;
}
}
boolean check_cross_first_line = false;
Line prev_division_line = null;
Line first_division_line = division_line_arr[0][0];
IntDirection first_direction = (IntDirection)first_division_line.direction();
Collection<Simplex> result_list = new LinkedList<Simplex>();
for (int inner_corner_no = 0; inner_corner_no < inner_corner_count; ++inner_corner_no)
{
Line next_division_line;
if (inner_corner_no == inner_simplex.arr.length - 1)
next_division_line = division_line_arr[0][0];
else
next_division_line = division_line_arr[inner_corner_no + 1][0];
Line[] curr_division_lines = division_line_arr[inner_corner_no];
if (curr_division_lines.length == 2)
{
// 2 division lines are nessesary (sharp corner).
// Construct an unbounded simplex from
// curr_division_lines[1] and curr_division_lines[0]
// and intersect it with the outer simplex
IntDirection curr_dir = (IntDirection)curr_division_lines[0].direction();
boolean merge_prev_division_line = false;
boolean merge_first_division_line = false;
if (prev_division_line != null)
{
IntDirection prev_dir = (IntDirection)prev_division_line.direction();
if (curr_dir.determinant(prev_dir) > 0)
{
// the previous division line may intersect
// curr_division_lines[0] inside p_divide_simplex
merge_prev_division_line = true;
}
}
if (!check_cross_first_line)
{
check_cross_first_line = (inner_corner_no > 0 &&
curr_dir.determinant(first_direction) > 0);
}
if (check_cross_first_line)
{
IntDirection curr_dir2 = (IntDirection)curr_division_lines[1].direction();
if (curr_dir2.determinant(first_direction) < 0)
{
// The current piece has an intersection area with the first
// piece.
// Add a line to tmp_polyline to prevent this
merge_first_division_line = true;
}
}
int piece_line_count = 2;
if (merge_prev_division_line)
++piece_line_count;
if (merge_first_division_line)
++piece_line_count;
Line[] piece_lines = new Line[piece_line_count];
piece_lines[0] = new Line(curr_division_lines[1].b, curr_division_lines[1].a);
piece_lines[1] = curr_division_lines[0];
int curr_line_no = 1;
if (merge_prev_division_line)
{
++curr_line_no;
piece_lines[curr_line_no] = prev_division_line;
}
if (merge_first_division_line)
{
++curr_line_no;
piece_lines[curr_line_no] =
new Line(first_division_line.b, first_division_line.a);
}
Simplex curr_piece = new Simplex(piece_lines);
result_list.add(curr_piece.intersection(p_outer_simplex));
}
// construct an unbounded simplex from next_division_line,
// inner_simplex.line [inner_corner_no] and the last current division line
// and intersect it with the outer simplex
boolean merge_next_division_line = !next_division_line.b.equals(next_division_line.a);
Line last_curr_division_line = curr_division_lines[curr_division_lines.length - 1];
IntDirection last_curr_dir = (IntDirection)last_curr_division_line.direction();
boolean merge_last_curr_division_line =
!last_curr_division_line.b.equals(last_curr_division_line.a);
boolean merge_prev_division_line = false;
boolean merge_first_division_line = false;
if (prev_division_line != null)
{
IntDirection prev_dir = (IntDirection)prev_division_line.direction();
if (last_curr_dir.determinant(prev_dir) > 0)
{
// the previous division line may intersect
// the last current division line inside p_divide_simplex
merge_prev_division_line = true;
}
}
if (!check_cross_first_line)
{
check_cross_first_line = inner_corner_no > 0 &&
last_curr_dir.determinant(first_direction) > 0 &&
last_curr_dir.get_vector().scalar_product(first_direction.get_vector()) < 0;
// scalar_product checked to ignore backcrossing at
// small inner_corner_no
}
if (check_cross_first_line)
{
IntDirection next_dir = (IntDirection)next_division_line.direction();
if(next_dir.determinant(first_direction) < 0)
{
// The current piece has an intersection area with the first piece.
// Add a line to tmp_polyline to prevent this
merge_first_division_line = true;
}
}
int piece_line_count = 1;
if (merge_next_division_line)
++piece_line_count;
if (merge_last_curr_division_line)
++piece_line_count;
if (merge_prev_division_line)
++piece_line_count;
if (merge_first_division_line)
++piece_line_count;
Line[] piece_lines = new Line[piece_line_count];
Line curr_line = inner_simplex.arr[inner_corner_no];
piece_lines[0] = new Line(curr_line.b, curr_line.a);
int curr_line_no = 0;
if (merge_next_division_line)
{
++curr_line_no;
piece_lines[curr_line_no] = new Line(next_division_line.b, next_division_line.a);
}
if (merge_last_curr_division_line)
{
++curr_line_no;
piece_lines[curr_line_no] = last_curr_division_line;
}
if (merge_prev_division_line)
{
++curr_line_no;
piece_lines[curr_line_no] = prev_division_line;
}
if (merge_first_division_line)
{
++curr_line_no;
piece_lines[curr_line_no] =
new Line(first_division_line.b, first_division_line.a);
}
Simplex curr_piece = new Simplex(piece_lines);
result_list.add(curr_piece.intersection(p_outer_simplex));
next_division_line = prev_division_line;
}
Simplex[] result = new Simplex[result_list.size()];
Iterator<Simplex> it = result_list.iterator();
for (int i = 0; i < result.length; ++i)
{
result[i] = it.next();
}
return result;
}
Simplex[] cutout_from(IntOctagon p_oct)
{
return cutout_from(p_oct.to_Simplex());
}
Simplex[] cutout_from(IntBox p_box)
{
return cutout_from(p_box.to_Simplex());
}
/**
* Removes lines, which are redundant in the definition of the
* shape of this simplex.
* Assumes that the lines of this simplex are sorted.
*/
Simplex remove_redundant_lines()
{
Line [] line_arr = new Line [arr.length];
// copy the sorted lines of arr into line_arr while skipping
// multiple lines
int new_length = 1;
line_arr[0] = arr[0];
Line prev = line_arr[0];
for (int i = 1; i < arr.length; ++i)
{
if (!arr[i].fast_equals(prev))
{
line_arr[new_length] = arr[i];
prev = line_arr[new_length];
++new_length;
}
}
Side [] intersection_sides = new Side [new_length];
// precalculated array , on which side of this line the previous and the
// next line do intersect
boolean try_again = new_length > 2;
int index_of_last_removed_line = new_length;
while(try_again)
{
try_again = false;
int prev_ind = new_length - 1;
int next_ind;
Line prev_line = line_arr[prev_ind];
Line curr_line = line_arr[0];
Line next_line;
for (int ind = 0; ind < new_length; ++ind)
{
if (ind == new_length - 1)
{
next_ind = 0;
}
else
{
next_ind = ind + 1;
}
next_line = line_arr[next_ind];
boolean remove_line = false;
IntDirection prev_dir = (IntDirection) prev_line.direction();
IntDirection next_dir = (IntDirection) next_line.direction();
double det = prev_dir.determinant(next_dir);
if (det != 0) // prev_line and next_line are not parallel
{
if (intersection_sides [ind] == null)
{
// intersection_sides [ind] not precalculated
intersection_sides [ind] = curr_line.side_of_intersection(prev_line, next_line);
}
if(det > 0 )
// direction of next_line is bigger than direction of prev_line
{
// if the intersection of prev_line and next_line
// is on the left of curr_line, curr_line does not
// contribute to the shape of the simplex
remove_line = (intersection_sides[ind] != Side.ON_THE_LEFT);
}
else
// direction of next_line is smaller than direction of prev_line
{
if (intersection_sides[ind] == Side.ON_THE_LEFT)
{
IntDirection curr_dir = (IntDirection) curr_line.direction();
if (prev_dir.determinant(curr_dir) > 0)
// direction of curr_line is bigger than direction of prev_line
{
// the halfplane defined by curr_line does not intersect
// with the simplex defined by prev_line and nex_line,
// hence this simplex must be empty
new_length = 0;
try_again = false;
break;
}
}
}
}
else // prev_line and next_line are parallel
{
if (prev_line.side_of(next_line.a) == Side.ON_THE_LEFT)
// prev_line is to the left of next_line,
// the halfplanes defined by prev_line and next_line
// do not intersect
{
new_length = 0;
try_again = false;
break;
}
}
if (remove_line)
{
try_again = true;
--new_length;
for (int i = ind; i < new_length; ++i)
{
line_arr [i] = line_arr [i + 1];
intersection_sides[i] = intersection_sides [i + 1];
}
if (new_length < 3)
{
try_again = false;
break;
}
// reset 3 precalculated intersection_sides
if (ind == 0)
{
prev_ind = new_length - 1;
}
intersection_sides [prev_ind] = null;
if (ind >= new_length)
{
next_ind = 0;
}
else
{
next_ind = ind;
}
intersection_sides [next_ind] = null;
--ind;
index_of_last_removed_line = ind;
}
else
{
prev_line = curr_line;
prev_ind = ind;
}
curr_line = next_line;
if( !try_again && ind >= index_of_last_removed_line)
// tried all lines without removing one
{
break;
}
}
}
if (new_length == 2)
{
if (line_arr[0].is_parallel(line_arr[1]))
{
if(line_arr[0].direction().equals(line_arr[1].direction()))
// one of the two remaining lines is redundant
{
if (line_arr[1].side_of(line_arr[0].a) == Side.ON_THE_LEFT)
{
line_arr[0] = line_arr[1];
}
--new_length;
}
else
// the two remaining lines have opposite direction
// the simplex may be empty
{
if (line_arr[1].side_of(line_arr[0].a) == Side.ON_THE_LEFT)
{
new_length = 0;
}
}
}
}
if (new_length == arr.length)
{
return this; // nothing removed
}
if (new_length == 0)
{
return Simplex.EMPTY;
}
Line [] result = new Line [new_length];
System.arraycopy(line_arr, 0, result, 0, new_length);
return new Simplex(result);
}
public boolean intersects(IntBox p_box)
{
return intersects(p_box.to_Simplex());
}
public boolean intersects(IntOctagon p_octagon)
{
return intersects(p_octagon.to_Simplex());
}
public boolean intersects(Circle p_circle)
{
return p_circle.intersects(this);
}
/**
* For each corner of this inner simplex 1 or 2 perpendicular
* projections onto lines of the outer simplex are constructed,
* so that the resulting pieces after cutting out the inner simplex
* are convex. 2 projections may be nessesary at sharp angle corners.
* Used in in the method cutout_from with parametertype Simplex.
*/
private Line[] calc_division_lines(int p_inner_corner_no, Simplex p_outer_simplex)
{
Line curr_inner_line = this.arr[p_inner_corner_no];
Line prev_inner_line;
if (p_inner_corner_no != 0)
prev_inner_line = this.arr[p_inner_corner_no - 1];
else
prev_inner_line = this.arr[arr.length - 1];
FloatPoint intersection = curr_inner_line.intersection_approx(prev_inner_line);
if (intersection.x >= Integer.MAX_VALUE)
{
System.out.println("Simplex.calc_division_lines: intersection expexted");
return null;
}
IntPoint inner_corner = intersection.round();
double c_tolerance = 0.0001;
boolean is_exact =
Math.abs(inner_corner.x - intersection.x) < c_tolerance
&& Math.abs(inner_corner.y - intersection.y) < c_tolerance;
if (!is_exact)
{
// it is assumed, that the corners of the original inner simplex are
// exact and the not exact corners come from the intersection of
// the inner simplex with the outer simplex.
// Because these corners lie on the border of the outer simplex,
// no division is nessesary
Line [] result = new Line[1];
result[0] = prev_inner_line;
return result;
}
IntDirection first_projection_dir = Direction.NULL;
IntDirection second_projection_dir = Direction.NULL;
IntDirection prev_inner_dir = (IntDirection) prev_inner_line.direction().opposite();
IntDirection next_inner_dir = (IntDirection) curr_inner_line.direction();
int outer_line_no = 0;
// search the first outer line, so that
// the perpendicular projection of the inner corner onto this
// line is visible from inner_corner to the left of prev_inner_line.
double min_distance = Integer.MAX_VALUE;
for (int ind = 0; ind < p_outer_simplex.arr.length; ++ind)
{
Line outer_line = p_outer_simplex.arr[outer_line_no];
IntDirection curr_projection_dir =
(IntDirection)inner_corner.perpendicular_direction(outer_line);
if (curr_projection_dir == Direction.NULL)
{
Line [] result = new Line[1];
result[0] = new Line(inner_corner, inner_corner);
return result;
}
boolean projection_visible = prev_inner_dir.determinant(curr_projection_dir) >= 0;
if (projection_visible)
{
double curr_distance = Math.abs(outer_line.signed_distance(inner_corner.to_float()));
boolean second_division_necessary =
curr_projection_dir.determinant(next_inner_dir) < 0;
// may occor at a sharp angle
IntDirection curr_second_projection_dir = curr_projection_dir;
if (second_division_necessary)
{
// search the first projection_dir between curr_projection_dir
// and next_inner_dir, that is visible from next_inner_line
boolean second_projection_visible = false;
int tmp_outer_line_no = outer_line_no;
while (!second_projection_visible)
{
if (tmp_outer_line_no == p_outer_simplex.arr.length - 1)
{
tmp_outer_line_no = 0;
}
else
{
++tmp_outer_line_no;
}
curr_second_projection_dir =
(IntDirection)inner_corner.perpendicular_direction(
p_outer_simplex.arr[tmp_outer_line_no]);
if (curr_second_projection_dir == Direction.NULL)
// inner corner is on outer_line
{
Line [] result = new Line[1];
result[0] = new Line(inner_corner, inner_corner);
return result;
}
if (curr_projection_dir.determinant(curr_second_projection_dir) < 0)
{
// curr_second_projection_dir not found;
// the angle between curr_projection_dir and
// curr_second_projection_dir would be already bigger
// than 180 degree
curr_distance = Integer.MAX_VALUE;
break;
}
second_projection_visible =
curr_second_projection_dir.determinant(next_inner_dir) >= 0;
}
curr_distance +=
Math.abs(p_outer_simplex.arr[tmp_outer_line_no].signed_distance(inner_corner.to_float()));
}
if (curr_distance < min_distance)
{
min_distance = curr_distance;
first_projection_dir = curr_projection_dir;
second_projection_dir = curr_second_projection_dir;
}
}
if (outer_line_no == p_outer_simplex.arr.length - 1)
{
outer_line_no = 0;
}
else
{
++outer_line_no;
}
}
if (min_distance == Integer.MAX_VALUE)
{
System.out.println("Simplex.calc_division_lines: division not found");
return null;
}
Line[] result;
if (first_projection_dir.equals(second_projection_dir))
{
result = new Line[1];
result[0] = new Line(inner_corner, first_projection_dir);
}
else
{
result = new Line[2];
result[0] = new Line(inner_corner, first_projection_dir);
result[1] = new Line(inner_corner, second_projection_dir);
}
return result;
}
private final Line[] arr;
/**
* the following fields are for storing precalculated data
*/
transient private Point[] precalculated_corners = null;
transient private FloatPoint[] precalculated_float_corners = null;
transient private IntBox precalculated_bounding_box = null;
transient private IntOctagon precalculated_bounding_octagon = null;
}