/*
* 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;
/**
*
* Implements functionality for convex shapes, whose borderline directions are
* multiples of 45 degree and defined with integer coordinates.
*
* @author Alfons Wirtz
*/
public class IntOctagon extends RegularTileShape implements java.io.Serializable
{
/**
* Reusable instance of an empty octagon.
*/
public static final IntOctagon EMPTY =
new IntOctagon(Limits.CRIT_INT, Limits.CRIT_INT, -Limits.CRIT_INT,
-Limits.CRIT_INT, Limits.CRIT_INT, -Limits.CRIT_INT,
Limits.CRIT_INT, -Limits.CRIT_INT);
/**
* Creates an IntOctagon from 8 integer values.
* p_lx is the smallest x value of the shape.
* p_ly is the smallest y value of the shape.
* p_rx is the biggest x valuje af the shape.
* p_uy is the biggest y value of the shape.
* p_ulx is the intersection of the upper left diagonal boundary line
* with the x axis.
* p_lrx is the intersection of the lower right diagonal boundary line
* with the x axis.
* p_llx is the intersection of the lower left diagonal boundary line
* with the x axis.
* p_urx is the intersection of the upper right diagonal boundary line
* with the x axis.
*/
public IntOctagon(int p_lx, int p_ly, int p_rx, int p_uy,
int p_ulx, int p_lrx, int p_llx, int p_urx)
{
lx = p_lx;
ly = p_ly;
rx = p_rx;
uy = p_uy;
ulx = p_ulx;
lrx = p_lrx;
llx = p_llx;
urx = p_urx;
}
public boolean is_empty()
{
return this == EMPTY;
}
public boolean is_IntOctagon()
{
return true;
}
public boolean is_bounded()
{
return true;
}
public boolean corner_is_bounded(int p_no)
{
return true;
}
public IntBox bounding_box()
{
return new IntBox(lx, ly, rx, uy);
}
public IntOctagon bounding_octagon()
{
return this;
}
public IntOctagon bounding_tile()
{
return this;
}
public int dimension()
{
if (this == EMPTY)
{
return -1;
}
int result;
if (rx > lx && uy > ly && lrx > ulx && urx > llx)
{
result = 2;
}
else if (rx == lx && uy == ly)
{
result = 0;
}
else
{
result = 1;
}
return result;
}
public IntPoint corner(int p_no)
{
int x;
int y;
switch (p_no)
{
case 0:
x = llx - ly;
y = ly;
break;
case 1:
x = lrx + ly;
y = ly;
break;
case 2:
x = rx;
y = rx - lrx;
break;
case 3:
x = rx;
y = urx - rx;
break;
case 4:
x = urx - uy;
y = uy;
break;
case 5:
x = ulx + uy;
y = uy;
break;
case 6:
x = lx;
y = lx - ulx;
break;
case 7:
x = lx;
y = llx - lx;
break;
default:
throw new IllegalArgumentException
("IntOctagon.corner: p_no out of range");
}
return new IntPoint(x,y);
}
/**
* Additional to the function corner() for performance reasons to avoid allocation of an IntPoint.
*/
public int corner_y(int p_no)
{
int y;
switch (p_no)
{
case 0:
y = ly;
break;
case 1:
y = ly;
break;
case 2:
y = rx - lrx;
break;
case 3:
y = urx - rx;
break;
case 4:
y = uy;
break;
case 5:
y = uy;
break;
case 6:
y = lx - ulx;
break;
case 7:
y = llx - lx;
break;
default:
throw new IllegalArgumentException
("IntOctagon.corner: p_no out of range");
}
return y;
}
/**
* Additional to the function corner() for performance reasons to avoid allocation of an IntPoint.
*/
public int corner_x(int p_no)
{
int x;
switch (p_no)
{
case 0:
x = llx - ly;
break;
case 1:
x = lrx + ly;
break;
case 2:
x = rx;
break;
case 3:
x = rx;
break;
case 4:
x = urx - uy;
break;
case 5:
x = ulx + uy;
break;
case 6:
x = lx;
break;
case 7:
x = lx;
break;
default:
throw new IllegalArgumentException
("IntOctagon.corner: p_no out of range");
}
return x;
}
public double area()
{
// calculate half of the absolute value of
// x0 (y1 - y7) + x1 (y2 - y0) + x2 (y3 - y1) + ...+ x7( y0 - y6)
// where xi, yi are the coordinates of the i-th corner of this Octagon.
// Overwrites the same implementation in TileShape for performence
// reasons to avoid Point allocation.
double result = (double) (llx - ly) * (double) (ly - llx + lx);
result += (double) (lrx + ly) * (double) (rx - lrx - ly);
result += (double) rx * (double) (urx - 2 * rx - ly + uy + lrx);
result += (double) (urx - uy) * (double) (uy - urx + rx);
result += (double) (ulx + uy) * (double) (lx - ulx - uy);
result += (double) lx * (double) (llx - 2 * lx - uy + ly + ulx);
return 0.5 * Math.abs(result);
}
public int border_line_count()
{
return 8;
}
public Line border_line(int p_no)
{
int a_x;
int a_y;
int b_x;
int b_y;
switch (p_no)
{
case 0:
// lower boundary line
a_x = 0;
a_y = ly;
b_x = 1;
b_y = ly;
break;
case 1:
// lower right boundary line
a_x = lrx;
a_y = 0;
b_x = lrx + 1;
b_y = 1;
break;
case 2:
// right boundary line
a_x = rx;
a_y = 0;
b_x = rx;
b_y = 1;
break;
case 3:
// upper right boundary line
a_x = urx;
a_y = 0;
b_x = urx - 1;
b_y = 1;
break;
case 4:
// upper boundary line
a_x = 0;
a_y = uy;
b_x = -1;
b_y = uy;
break;
case 5:
// upper left boundary line
a_x = ulx;
a_y = 0;
b_x = ulx - 1;
b_y = -1;
break;
case 6:
// left boundary line
a_x = lx;
a_y = 0;
b_x = lx;
b_y = -1;
break;
case 7:
// lower left boundary line
a_x = llx;
a_y = 0;
b_x = llx + 1;
b_y = -1;
break;
default:
throw new IllegalArgumentException
("IntOctagon.edge_line: p_no out of range");
}
return new Line(a_x, a_y, b_x, b_y);
}
public IntOctagon translate_by(Vector p_rel_coor)
{
// This function is at the moment only implemented for Vectors
// with integer coordinates.
// The general implementation is still missing.
if (p_rel_coor.equals(Vector.ZERO))
{
return this;
}
IntVector rel_coor = (IntVector) p_rel_coor;
return new IntOctagon(lx + rel_coor.x, ly + rel_coor.y, rx + rel_coor.x, uy + rel_coor.y,
ulx + rel_coor.x - rel_coor.y, lrx + rel_coor.x - rel_coor.y,
llx + rel_coor.x + rel_coor.y, urx + rel_coor.x + rel_coor.y);
}
public double max_width()
{
double width_1 = Math.max(rx - lx, uy - ly);
double width2 = Math.max(urx - llx, lrx - ulx);
double result = Math.max(width_1, width2/ Limits.sqrt2);
return result;
}
public double min_width()
{
double width_1 = Math.min(rx - lx, uy - ly);
double width2 = Math.min(urx - llx, lrx - ulx);
double result = Math.min(width_1, width2/ Limits.sqrt2);
return result;
}
public IntOctagon offset(double p_distance)
{
int width = (int) Math.round(p_distance);
if (width == 0)
{
return this;
}
int dia_width = (int) Math.round(Limits.sqrt2 * p_distance);
IntOctagon result =
new IntOctagon(lx - width, ly - width, rx + width, uy + width,
ulx - dia_width, lrx + dia_width,
llx - dia_width, urx + dia_width);
return result.normalize();
}
public IntOctagon enlarge(double p_offset)
{
return offset(p_offset);
}
public boolean contains(RegularTileShape p_other)
{
return p_other.is_contained_in(this);
}
public RegularTileShape union(RegularTileShape p_other)
{
return p_other.union(this);
}
public TileShape intersection(TileShape p_other)
{
return p_other.intersection(this);
}
public IntOctagon normalize()
{
if (lx > rx || ly > uy || llx > urx || ulx > lrx)
{
return EMPTY;
}
int new_lx = lx;
int new_rx = rx;
int new_ly = ly;
int new_uy = uy;
int new_llx = llx;
int new_ulx = ulx;
int new_lrx = lrx;
int new_urx = urx;
if (new_lx < new_llx - new_uy)
// the point new_lx, new_uy is the the lower left border line of
// this octagon
// change new_lx , that the the lower left border line runs through
// this point
{
new_lx = new_llx - new_uy;
}
if (new_lx < new_ulx + new_ly)
// the point new_lx, new_ly is above the the upper left border line of
// this octagon
// change new_lx , that the the upper left border line runs through
// this point
{
new_lx = new_ulx + new_ly;
}
if (new_rx > new_urx - new_ly)
// the point new_rx, new_ly is above the the upper right border line of
// this octagon
// change new_rx , that the the upper right border line runs through
// this point
{
new_rx = new_urx - new_ly;
}
if (new_rx > new_lrx + new_uy)
// the point new_rx, new_uy is below the the lower right border line of
// this octagon
// change rx , that the the lower right border line runs through
// this point
{
new_rx = new_lrx + new_uy;
}
if (new_ly < new_lx - new_lrx)
// the point lx, ly is below the lower right border line of this
// octagon
// change ly, so that the lower right border line runs through
// this point
{
new_ly = new_lx - new_lrx;
}
if (new_ly < new_llx - new_rx)
// the point rx, ly is below the lower left border line of
// this octagon.
// change ly, so that the lower left border line runs through
// this point
{
new_ly = new_llx - new_rx;
}
if (new_uy > new_urx - new_lx)
// the point lx, uy is above the upper right border line of
// this octagon.
// Change the uy, so that the upper right border line runs through
// this point.
{
new_uy = new_urx - new_lx;
}
if (new_uy > new_rx - new_ulx)
// the point rx, uy is above the upper left border line of
// this octagon.
// Change the uy, so that the upper left border line runs through
// this point.
{
new_uy = new_rx - new_ulx;
}
if (new_llx - new_lx < new_ly)
// The point lx, ly is above the lower left border line of
// this octagon.
// Change the lower left line, so that it runs through this point.
{
new_llx = new_lx + new_ly;
}
if (new_rx - new_lrx < new_ly)
// the point rx, ly is above the lower right border line of
// this octagon.
// Change the lower right line, so that it runs through this point.
{
new_lrx = new_rx - new_ly;
}
if (new_urx - new_rx > new_uy)
// the point rx, uy is below the upper right border line of p_oct.
// Change the upper right line, so that it runs through this point.
{
new_urx = new_uy + new_rx;
}
if (new_lx - new_ulx > new_uy)
// the point lx, uy is below the upper left border line of
// this octagon.
// Change the upper left line, so that it runs through this point.
{
new_ulx = new_lx - new_uy;
}
int diag_upper_y = (int)Math.ceil((new_urx - new_ulx) / 2.0);
if (new_uy > diag_upper_y)
// the intersection of the upper right and the upper left border
// line is below new_uy. Adjust new_uy to diag_upper_y.
{
new_uy = diag_upper_y;
}
int diag_lower_y = (int)Math.floor((new_llx - new_lrx) / 2.0);
if (new_ly < diag_lower_y)
// the intersection of the lower right and the lower left border
// line is above new_ly. Adjust new_ly to diag_lower_y.
{
new_ly = diag_lower_y;
}
int diag_right_x = (int)Math.ceil((new_urx + new_lrx)/ 2.0);
if (new_rx > diag_right_x)
// the intersection of the upper right and the lower right border
// line is to the left of right x. Adjust new_rx to diag_right_x.
{
new_rx = diag_right_x;
}
int diag_left_x = (int)Math.floor((new_llx + new_ulx) / 2.0);
if (new_lx < diag_left_x)
// the intersection of the lower left and the upper left border
// line is to the right of left x. Ajust new_lx to diag_left_x.
{
new_lx = diag_left_x;
}
if (new_lx > new_rx || new_ly > new_uy || new_llx > new_urx
|| new_ulx > new_lrx)
{
return EMPTY;
}
return new IntOctagon(new_lx, new_ly, new_rx, new_uy, new_ulx,
new_lrx, new_llx, new_urx);
}
/**
* Checks, if this IntOctagon is normalized.
*/
public boolean is_normalized()
{
IntOctagon on = this.normalize();
boolean result =
lx == on.lx && ly == on.ly && rx == on.rx && uy == on.uy &&
llx == on.llx && lrx == on.lrx && ulx == on.ulx && urx == on.urx;
return result;
}
public Simplex to_Simplex()
{
if (is_empty())
{
return Simplex.EMPTY;
}
if (precalculated_to_simplex == null)
{
Line [] line_arr = new Line[8];
for (int i = 0; i < 8; ++i)
{
line_arr[i] = border_line(i);
}
Simplex curr_simplex = new Simplex(line_arr);
precalculated_to_simplex = curr_simplex.remove_redundant_lines();
}
return precalculated_to_simplex;
}
public RegularTileShape bounding_shape(ShapeBoundingDirections p_dirs)
{
return p_dirs.bounds(this);
}
public boolean intersects(Shape p_other)
{
return p_other.intersects(this);
}
/**
* Returns true, if p_point is contained in this octagon.
* Because of the parameter type FloatPoint, the function may not
* be exact close to the border.
*/
public boolean contains(FloatPoint p_point)
{
if (lx > p_point.x || ly > p_point.y
|| rx < p_point.x || uy < p_point.y)
{
return false;
}
double tmp_1 = p_point.x - p_point.y;
double tmp_2 = p_point.x + p_point.y;
if (ulx > tmp_1 || lrx < tmp_1 || llx > tmp_2 || urx < tmp_2)
{
return false;
}
return true;
}
/**
* Calculates the side of the point (p_x, p_y) of the border line with index p_border_line_no.
* The border lines are located in counterclock sense around this octagon.
*/
public Side side_of_border_line(int p_x, int p_y, int p_border_line_no)
{
int tmp;
if (p_border_line_no == 0)
{
tmp = this.ly - p_y;
}
else if (p_border_line_no == 2)
{
tmp = p_x - this.rx;
}
else if (p_border_line_no == 4)
{
tmp = p_y - this.uy;
}
else if (p_border_line_no == 6)
{
tmp = this.lx - p_x;
}
else if (p_border_line_no == 1)
{
tmp = p_x - p_y - this.lrx;
}
else if (p_border_line_no == 3)
{
tmp = p_x + p_y - this.urx;
}
else if (p_border_line_no == 5)
{
tmp = this.ulx + p_y - p_x;
}
else if (p_border_line_no == 7)
{
tmp = this.llx - p_x - p_y;
}
else
{
System.out.println("IntOctagon.side_of_border_line: p_border_line_no out of range");
tmp = 0;
}
Side result;
if (tmp < 0)
{
result = Side.ON_THE_LEFT;
}
else if (tmp > 0)
{
result = Side.ON_THE_RIGHT;
}
else
{
result = Side.COLLINEAR;
}
return result;
}
Simplex intersection(Simplex p_other)
{
return p_other.intersection(this);
}
public IntOctagon intersection(IntOctagon p_other)
{
IntOctagon result =
new IntOctagon(Math.max(lx, p_other.lx), Math.max(ly, p_other.ly),
Math.min(rx, p_other.rx), Math.min(uy, p_other.uy),
Math.max(ulx, p_other.ulx), Math.min(lrx, p_other.lrx),
Math.max(llx, p_other.llx), Math.min(urx, p_other.urx));
return result.normalize();
}
IntOctagon intersection(IntBox p_other)
{
return intersection(p_other.to_IntOctagon());
}
/**
* checkes if this (normalized) octagon is contained in p_box
*/
public boolean is_contained_in(IntBox p_box)
{
return (lx >= p_box.ll.x && ly >= p_box.ll.y &&
rx <= p_box.ur.x && uy <=p_box.ur.y);
}
public boolean is_contained_in(IntOctagon p_other)
{
boolean result = lx >= p_other.lx && ly >= p_other.ly &&
rx <= p_other.rx && uy <= p_other.uy &&
llx >= p_other.llx && ulx >= p_other.ulx &&
lrx <= p_other.lrx && urx <= p_other.urx;
return result;
}
public IntOctagon union(IntOctagon p_other)
{
IntOctagon result =
new IntOctagon(Math.min(lx, p_other.lx), Math.min(ly, p_other.ly),
Math.max(rx, p_other.rx), Math.max(uy, p_other.uy),
Math.min(ulx, p_other.ulx), Math.max(lrx, p_other.lrx),
Math.min(llx, p_other.llx), Math.max(urx, p_other.urx));
return result;
}
public boolean intersects(IntBox p_other)
{
return intersects(p_other.to_IntOctagon());
}
/**
* checks, if two normalized Octagons intersect.
*/
public boolean intersects(IntOctagon p_other)
{
int is_lx;
int is_rx;
if (p_other.lx > this.lx)
{
is_lx = p_other.lx;
}
else
{
is_lx = this.lx;
}
if (p_other.rx < this.rx)
{
is_rx = p_other.rx;
}
else
{
is_rx = this.rx;
}
if (is_lx > is_rx)
{
return false;
}
int is_ly;
int is_uy;
if (p_other.ly > this.ly)
{
is_ly = p_other.ly;
}
else
{
is_ly = this.ly;
}
if (p_other.uy < this.uy)
{
is_uy = p_other.uy;
}
else
{
is_uy = this.uy;
}
if (is_ly > is_uy)
{
return false;
}
int is_llx;
int is_urx;
if (p_other.llx > this.llx)
{
is_llx = p_other.llx;
}
else
{
is_llx = this.llx;
}
if (p_other.urx < this.urx)
{
is_urx = p_other.urx;
}
else
{
is_urx = this.urx;
}
if (is_llx > is_urx)
{
return false;
}
int is_ulx;
int is_lrx;
if (p_other.ulx > this.ulx)
{
is_ulx = p_other.ulx;
}
else
{
is_ulx = this.ulx;
}
if (p_other.lrx < this.lrx)
{
is_lrx = p_other.lrx;
}
else
{
is_lrx = this.lrx;
}
if (is_ulx > is_lrx)
{
return false;
}
return true;
}
/**
* Returns true, if this octagon intersects with p_other and the intersection is 2-dimensional.
*/
public boolean overlaps(IntOctagon p_other)
{
int is_lx;
int is_rx;
if (p_other.lx > this.lx)
{
is_lx = p_other.lx;
}
else
{
is_lx = this.lx;
}
if (p_other.rx < this.rx)
{
is_rx = p_other.rx;
}
else
{
is_rx = this.rx;
}
if (is_lx >= is_rx)
{
return false;
}
int is_ly;
int is_uy;
if (p_other.ly > this.ly)
{
is_ly = p_other.ly;
}
else
{
is_ly = this.ly;
}
if (p_other.uy < this.uy)
{
is_uy = p_other.uy;
}
else
{
is_uy = this.uy;
}
if (is_ly >= is_uy)
{
return false;
}
int is_llx;
int is_urx;
if (p_other.llx > this.llx)
{
is_llx = p_other.llx;
}
else
{
is_llx = this.llx;
}
if (p_other.urx < this.urx)
{
is_urx = p_other.urx;
}
else
{
is_urx = this.urx;
}
if (is_llx >= is_urx)
{
return false;
}
int is_ulx;
int is_lrx;
if (p_other.ulx > this.ulx)
{
is_ulx = p_other.ulx;
}
else
{
is_ulx = this.ulx;
}
if (p_other.lrx < this.lrx)
{
is_lrx = p_other.lrx;
}
else
{
is_lrx = this.lrx;
}
if (is_ulx >= is_lrx)
{
return false;
}
return true;
}
public boolean intersects(Simplex p_other)
{
return p_other.intersects(this);
}
public boolean intersects(Circle p_other)
{
return p_other.intersects(this);
}
public IntOctagon union(IntBox p_other)
{
return union(p_other.to_IntOctagon());
}
/**
* computes the x value of the left boundary of this Octagon at p_y
*/
public int left_x_value(int p_y)
{
int result = Math.max(lx, ulx + p_y);
return Math.max(result, llx - p_y);
}
/**
* computes the x value of the right boundary of this Octagon at p_y
*/
public int right_x_value(int p_y)
{
int result = Math.min(rx, urx - p_y);
return Math.min(result, lrx + p_y);
}
/**
* computes the y value of the lower boundary of this Octagon at p_x
*/
public int lower_y_value(int p_x)
{
int result = Math.max(ly, llx - p_x);
return Math.max(result, p_x - lrx);
}
/**
* computes the y value of the upper boundary of this Octagon at p_x
*/
public int upper_y_value(int p_x)
{
int result = Math.min(uy, p_x - ulx);
return Math.min(result, urx - p_x);
}
public Side compare(RegularTileShape p_other, int p_edge_no)
{
Side result = p_other.compare(this, p_edge_no);
return result.negate();
}
public Side compare(IntOctagon p_other, int p_edge_no)
{
Side result;
switch (p_edge_no)
{
case 0:
// compare the lower edge line
if (ly > p_other.ly)
{
result = Side.ON_THE_LEFT;
}
else if (ly < p_other.ly)
{
result = Side.ON_THE_RIGHT;
}
else
{
result = Side.COLLINEAR;
}
break;
case 1:
// compare the lower right edge line
if (lrx < p_other.lrx)
{
result = Side.ON_THE_LEFT;
}
else if (lrx > p_other.lrx)
{
result = Side.ON_THE_RIGHT;
}
else
{
result = Side.COLLINEAR;
}
break;
case 2:
// compare the right edge line
if (rx < p_other.rx)
{
result = Side.ON_THE_LEFT;
}
else if (rx > p_other.rx)
{
result = Side.ON_THE_RIGHT;
}
else
{
result = Side.COLLINEAR;
}
break;
case 3:
// compare the upper right edge line
if (urx < p_other.urx)
{
result = Side.ON_THE_LEFT;
}
else if (urx > p_other.urx)
{
result = Side.ON_THE_RIGHT;
}
else
{
result = Side.COLLINEAR;
}
break;
case 4:
// compare the upper edge line
if (uy < p_other.uy)
{
result = Side.ON_THE_LEFT;
}
else if (uy > p_other.uy)
{
result = Side.ON_THE_RIGHT;
}
else
{
result = Side.COLLINEAR;
}
break;
case 5:
// compare the upper left edge line
if (ulx > p_other.ulx)
{
result = Side.ON_THE_LEFT;
}
else if (ulx < p_other.ulx)
{
result = Side.ON_THE_RIGHT;
}
else
{
result = Side.COLLINEAR;
}
break;
case 6:
// compare the left edge line
if (lx > p_other.lx)
{
result = Side.ON_THE_LEFT;
}
else if (lx < p_other.lx)
{
result = Side.ON_THE_RIGHT;
}
else
{
result = Side.COLLINEAR;
}
break;
case 7:
// compare the lower left edge line
if (llx > p_other.llx)
{
result = Side.ON_THE_LEFT;
}
else if (llx < p_other.llx)
{
result = Side.ON_THE_RIGHT;
}
else
{
result = Side.COLLINEAR;
}
break;
default:
throw new IllegalArgumentException
("IntBox.compare: p_edge_no out of range");
}
return result;
}
public Side compare(IntBox p_other, int p_edge_no)
{
return compare(p_other.to_IntOctagon(), p_edge_no);
}
public int border_line_index(Line p_line)
{
System.out.println("edge_index_of_line not yet implemented for octagons");
return -1;
}
/**
* Calculates the border point of this octagon from p_point into the 45 degree direction p_dir.
* If this border point is not an IntPoint, the nearest outside IntPoint of the octagon is returned.
*/
public IntPoint border_point(IntPoint p_point, FortyfiveDegreeDirection p_dir)
{
int result_x;
int result_y;
switch (p_dir)
{
case RIGHT:
result_x = Math.min(rx, urx - p_point.y);
result_x = Math.min(result_x, lrx + p_point.y);
result_y = p_point.y;
break;
case LEFT:
result_x = Math.max(lx, ulx + p_point.y);
result_x = Math.max(result_x, llx - p_point.y);
result_y = p_point.y;
break;
case UP:
result_x = p_point.x;
result_y = Math.min(uy, p_point.x - ulx);
result_y = Math.min(result_y, urx - p_point.x);
break;
case DOWN:
result_x = p_point.x;
result_y = Math.max(ly, llx - p_point.x);
result_y = Math.max(result_y, p_point.x - lrx);
break;
case RIGHT45:
result_x = (int) (Math.ceil(0.5 * (p_point.x - p_point.y + urx)));
result_x = Math.min(result_x, rx);
result_x = Math.min(result_x, p_point.x - p_point.x + uy);
result_y = p_point.y - p_point.x + result_x;
break;
case UP45:
result_x = (int)(Math.floor(0.5 * (p_point.x + p_point.y + ulx)));
result_x = Math.max(result_x, lx);
result_x = Math.max(result_x, p_point.x + p_point.y - uy);
result_y = p_point.y + p_point.x - result_x;
break;
case LEFT45:
result_x = (int)(Math.floor(0.5 * (p_point.x - p_point.y + llx)));
result_x = Math.max(result_x, lx);
result_x = Math.max(result_x, p_point.x - p_point.y + ly);
result_y = p_point.y - p_point.x + result_x;
break;
case DOWN45:
result_x = (int) (Math.ceil(0.5 * (p_point.x + p_point.y + lrx)));
result_x = Math.min(result_x, rx);
result_x = Math.min(result_x, p_point.x + p_point.y - ly);
result_y = p_point.y + p_point.x - result_x;
break;
default:
System.out.println("IntOctagon.border_point: unexpected 45 degree direction");
result_x = 0;
result_y = 0;
}
return new IntPoint(result_x, result_y);
}
/**
* Calculates the sorted p_max_result_points nearest points on the
* border of this octagon in the 45-degree directions.
* p_point is assumed to be located in the interiour of this octagon.
*/
public IntPoint[] nearest_border_projections(IntPoint p_point, int p_max_result_points)
{
if (!this.contains(p_point) || p_max_result_points <= 0)
{
return new IntPoint[0];
}
p_max_result_points = Math.min(p_max_result_points, 8);
IntPoint [] result = new IntPoint[p_max_result_points];
double [] min_dist = new double [p_max_result_points];
for (int i = 0; i < p_max_result_points; ++i)
{
min_dist[i] = Double.MAX_VALUE;
}
FloatPoint inside_point = p_point.to_float();
for (FortyfiveDegreeDirection curr_dir : FortyfiveDegreeDirection.values())
{
IntPoint curr_border_point = border_point(p_point, curr_dir);
double curr_dist = inside_point.distance_square(curr_border_point.to_float());
for (int i = 0; i < p_max_result_points; ++i)
{
if (curr_dist < min_dist[i])
{
for (int k = p_max_result_points - 1; k > i; --k)
{
min_dist[k] = min_dist[k - 1];
result[k] = result[k - 1];
}
min_dist[i] = curr_dist;
result[i] = curr_border_point;
break;
}
}
}
return result;
}
Side border_line_side_of( FloatPoint p_point, int p_line_no, double p_tolerance)
{
Side result;
if (p_line_no == 0)
{
if (p_point.y > this.ly + p_tolerance)
{
result = Side.ON_THE_RIGHT;
}
else if (p_point.y < this.ly - p_tolerance)
{
result = Side.ON_THE_LEFT;
}
else
{
result = Side.COLLINEAR;
}
}
else if (p_line_no == 2)
{
if (p_point.x < this.rx - p_tolerance)
{
result = Side.ON_THE_RIGHT;
}
else if (p_point.x > this.rx + p_tolerance)
{
result = Side.ON_THE_LEFT;
}
else
{
result = Side.COLLINEAR;
}
}
else if (p_line_no == 4)
{
if (p_point.y < this.uy - p_tolerance)
{
result = Side.ON_THE_RIGHT;
}
else if (p_point.y > this.uy + p_tolerance )
{
result = Side.ON_THE_LEFT;
}
else
{
result = Side.COLLINEAR;
}
}
else if (p_line_no == 6)
{
if (p_point.x > this.lx + p_tolerance )
{
result = Side.ON_THE_RIGHT;
}
else if (p_point.x < this.lx - p_tolerance )
{
result = Side.ON_THE_LEFT;
}
else
{
result = Side.COLLINEAR;
}
}
else if (p_line_no == 1)
{
double tmp = p_point.y - p_point.x + lrx;
if (tmp > p_tolerance)
// the p_point is above the the lower right border line of this octagon
{
result = Side.ON_THE_RIGHT;
}
else if (tmp < -p_tolerance)
// the p_point is below the the lower right border line of this octagon
{
result = Side.ON_THE_LEFT;
}
else
{
result = Side.COLLINEAR;
}
}
else if (p_line_no == 3)
{
double tmp = p_point.x + p_point.y - urx;
if (tmp < -p_tolerance)
{
// the p_point is below the the upper right border line of this octagon
result = Side.ON_THE_RIGHT;
}
else if (tmp > p_tolerance)
{
// the p_point is above the the upper right border line of this octagon
result = Side.ON_THE_LEFT;
}
else
{
result = Side.COLLINEAR;
}
}
else if (p_line_no == 5)
{
double tmp = p_point.y - p_point.x + ulx;
if (tmp < -p_tolerance)
// the p_point is below the the upper left border line of this octagon
{
result = Side.ON_THE_RIGHT;
}
else if (tmp > p_tolerance)
// the p_point is above the the upper left border line of this octagon
{
result = Side.ON_THE_LEFT;
}
else
{
result = Side.COLLINEAR;
}
}
else if (p_line_no == 7)
{
double tmp = p_point.x + p_point.y - llx;
if (tmp > p_tolerance)
{
// the p_point is above the the lower left border line of this octagon
result = Side.ON_THE_RIGHT;
}
else if (tmp < -p_tolerance)
{
// the p_point is below the the lower left border line of this octagon
result = Side.ON_THE_LEFT;
}
else
{
result = Side.COLLINEAR;
}
}
else
{
System.out.println("IntOctagon.border_line_side_of: p_line_no out of range");
result = Side.COLLINEAR;
}
return result;
}
/**
* Checks, if this octagon can be converted to an IntBox.
*/
public boolean is_IntBox()
{
if (llx != lx + ly)
return false;
if (lrx != rx - ly)
return false;
if (urx != rx + uy)
return false;
if (ulx != lx - uy)
return false;
return true;
}
public TileShape simplify()
{
if (this.is_IntBox())
{
return this.bounding_box();
}
return this;
}
public TileShape[] cutout(TileShape p_shape)
{
return p_shape.cutout_from(this);
}
/**
* Divide p_d minus this octagon into 8 convex pieces,
* from which 4 have cut off a corner.
*/
IntOctagon[] cutout_from(IntBox p_d)
{
IntOctagon c = this.intersection(p_d);
if (this.is_empty() || c.dimension() < this.dimension())
{
// there is only an overlap at the border
IntOctagon [] result = new IntOctagon[1];
result[0] = p_d.to_IntOctagon();
return result;
}
IntBox [] boxes = new IntBox[4];
// construct left box
boxes[0] = new IntBox(p_d.ll.x, c.llx - c.lx, c.lx, c.lx - c.ulx);
// construct right box
boxes[1] = new IntBox(c.rx, c.rx - c.lrx, p_d.ur.x, c.urx - c.rx);
// construct lower box
boxes[2] = new IntBox(c.llx - c.ly, p_d.ll.y, c.lrx + c.ly, c.ly);
// construct upper box
boxes[3] = new IntBox(c.ulx + c.uy, c.uy, c.urx - c.uy, p_d.ur.y);
IntOctagon[] octagons = new IntOctagon[4];
// construct upper left octagon
IntOctagon curr_oct = new IntOctagon(p_d.ll.x, boxes[0].ur.y, boxes[3].ll.x,
p_d.ur.y, -Limits.CRIT_INT, c.ulx, -Limits.CRIT_INT, Limits.CRIT_INT);
octagons[0] = curr_oct.normalize();
// construct lower left octagon
curr_oct = new IntOctagon(p_d.ll.x, p_d.ll.y, boxes[2].ll.x, boxes[0].ll.y,
-Limits.CRIT_INT, Limits.CRIT_INT, -Limits.CRIT_INT, c.llx);
octagons[1] = curr_oct.normalize();
// construct lower right octagon
curr_oct = new IntOctagon(boxes[2].ur.x, p_d.ll.y, p_d.ur.x, boxes[1].ll.y,
c.lrx, Limits.CRIT_INT, -Limits.CRIT_INT, Limits.CRIT_INT);
octagons[2] = curr_oct.normalize();
// construct upper right octagon
curr_oct = new IntOctagon(boxes[3].ur.x, boxes[1].ur.y, p_d.ur.x, p_d.ur.y,
-Limits.CRIT_INT, Limits.CRIT_INT, c.urx, Limits.CRIT_INT);
octagons[3] = curr_oct.normalize();
// optimise the result to minimum cumulative circumference
IntBox b = boxes[0];
IntOctagon o = octagons[0];
if (b.ur.x - b.ll.x > o.uy - o.ly)
{
// switch the horizontal upper left divide line to vertical
boxes[0] = new IntBox(b.ll.x, b.ll.y, b.ur.x, o.uy);
curr_oct = new IntOctagon(b.ur.x, o.ly, o.rx, o.uy, o.ulx, o.lrx, o.llx, o.urx);
octagons[0] = curr_oct.normalize();
}
b = boxes[3];
o = octagons[0];
if (b.ur.y - b.ll.y > o.rx - o.lx)
{
// switch the vertical upper left divide line to horizontal
boxes[3] = new IntBox(o.lx, b.ll.y, b.ur.x, b.ur.y);
curr_oct = new IntOctagon(o.lx, o.ly, o.rx, b.ll.y, o.ulx, o.lrx, o.llx, o.urx);
octagons[0] = curr_oct.normalize();
}
b = boxes[3];
o = octagons[3];
if (b.ur.y - b.ll.y > o.rx - o.lx)
{
// switch the vertical upper right divide line to horizontal
boxes[3] = new IntBox(b.ll.x, b.ll.y, o.rx, b.ur.y);
curr_oct = new IntOctagon(o.lx, o.ly, o.rx, o.uy, o.ulx, o.lrx, o.llx, o.urx);
octagons[3] = curr_oct.normalize();
}
b = boxes[1];
o = octagons[3];
if (b.ur.x - b.ll.x > o.uy - o.ly)
{
// switch the horizontal upper right divide line to vertical
boxes[1] = new IntBox(b.ll.x, b.ll.y, b.ur.x, o.uy);
curr_oct = new IntOctagon(o.lx, o.ly, b.ll.x, o.uy, o.ulx, o.lrx, o.llx, o.urx);
octagons[3] = curr_oct.normalize();
}
b = boxes[1];
o = octagons[2];
if (b.ur.x - b.ll.x > o.uy - o.ly)
{
// switch the horizontal lower right divide line to vertical
boxes[1] = new IntBox(b.ll.x, o.ly, b.ur.x, b.ur.y);
curr_oct = new IntOctagon(o.lx, o.ly, b.ll.x, o.uy, o.ulx, o.lrx, o.llx, o.urx);
octagons[2] = curr_oct.normalize();
}
b = boxes[2];
o = octagons[2];
if (b.ur.y - b.ll.y > o.rx - o.lx)
{
// switch the vertical lower right divide line to horizontal
boxes[2] = new IntBox(b.ll.x, b.ll.y, o.rx, b.ur.y);
curr_oct = new IntOctagon(o.lx, b.ur.y, o.rx, o.uy, o.ulx, o.lrx, o.llx, o.urx);
octagons[2] = curr_oct.normalize();
}
b = boxes[2];
o = octagons[1];
if (b.ur.y - b.ll.y > o.rx - o.lx)
{
// switch the vertical lower left divide line to horizontal
boxes[2] = new IntBox(o.lx, b.ll.y, b.ur.x, b.ur.y);
curr_oct = new IntOctagon(o.lx, b.ur.y, o.rx, o.uy, o.ulx, o.lrx, o.llx, o.urx);
octagons[1] = curr_oct.normalize();
}
b = boxes[0];
o = octagons[1];
if (b.ur.x - b.ll.x > o.uy - o.ly)
{
// switch the horizontal lower left divide line to vertical
boxes[0] = new IntBox(b.ll.x, o.ly, b.ur.x, b.ur.y);
curr_oct = new IntOctagon(b.ur.x, o.ly, o.rx, o.uy, o.ulx, o.lrx, o.llx, o.urx);
octagons[1] = curr_oct.normalize();
}
IntOctagon[] result = new IntOctagon[8];
// add the 4 boxes to the result
for (int i = 0; i < 4; ++i)
{
result[i] = boxes[i].to_IntOctagon();
}
// add the 4 octagons to the result
for (int i = 0; i < 4; ++i)
{
result[4 + i] = octagons[i];
}
return result;
}
/**
* Divide p_divide_octagon minus cut_octagon into 8 convex
* pieces without sharp angles.
*/
IntOctagon[] cutout_from(IntOctagon p_d)
{
IntOctagon c = this.intersection(p_d);
if (this.is_empty() || c.dimension() < this.dimension())
{
// there is only an overlap at the border
IntOctagon [] result = new IntOctagon[1];
result[0] = p_d;
return result;
}
IntOctagon [] result = new IntOctagon[8];
int tmp = c.llx - c.lx;
result[0] = new
IntOctagon(p_d.lx, tmp, c.lx, c.lx - c.ulx, p_d.ulx, p_d.lrx, p_d.llx, p_d.urx);
int tmp2 = c.llx - c.ly;
result[1] = new
IntOctagon(p_d.lx, p_d.ly, tmp2, tmp, p_d.ulx, p_d.lrx, p_d.llx, c.llx);
tmp = c.lrx + c.ly;
result[2] = new
IntOctagon(tmp2, p_d.ly, tmp, c.ly, p_d.ulx, p_d.lrx, p_d.llx, p_d.urx);
tmp2 = c.rx - c.lrx;
result[3] = new
IntOctagon(tmp, p_d.ly, p_d.rx, tmp2, c.lrx, p_d.lrx, p_d.llx, p_d.urx);
tmp = c.urx - c.rx;
result[4] = new
IntOctagon(c.rx, tmp2, p_d.rx, tmp, p_d.ulx, p_d.lrx, p_d.llx, p_d.urx);
tmp2 = c.urx - c.uy;
result[5] = new
IntOctagon(tmp2, tmp, p_d.rx, p_d.uy, p_d.ulx, p_d.lrx, c.urx, p_d.urx);
tmp = c.ulx + c.uy;
result[6] = new
IntOctagon(tmp, c.uy, tmp2, p_d.uy, p_d.ulx, p_d.lrx, p_d.llx, p_d.urx);
tmp2 = c.lx - c.ulx;
result[7] = new
IntOctagon(p_d.lx, tmp2, tmp, p_d.uy, p_d.ulx, c.ulx, p_d.llx, p_d.urx);
for (int i = 0; i < 8; ++i)
{
result[i] = result[i].normalize();
}
IntOctagon curr_1 = result[0];
IntOctagon curr_2 = result[7];
if (!(curr_1.is_empty() || curr_2.is_empty()) &&
curr_1.rx - curr_1.left_x_value(curr_1.uy)
> curr_2.upper_y_value(curr_1.rx) - curr_2.ly)
{
// switch the horizontal upper left divide line to vertical
curr_1 = new IntOctagon(Math.min(curr_1.lx, curr_2.lx), curr_1.ly,
curr_1.rx, curr_2.uy, curr_2.ulx, curr_1.lrx,curr_1.llx, curr_2.urx);
curr_2 = new IntOctagon(curr_1.rx, curr_2.ly, curr_2.rx, curr_2.uy,
curr_2.ulx, curr_2.lrx, curr_2.llx, curr_2.urx);
result[0] = curr_1.normalize();
result[7] = curr_2.normalize();
}
curr_1 = result[7];
curr_2 = result[6];
if (!(curr_1.is_empty() || curr_2.is_empty()) &&
curr_2.upper_y_value(curr_1.rx) - curr_2.ly
> curr_1.rx - curr_1.left_x_value(curr_2.ly))
// switch the vertical upper left divide line to horizontal
{
curr_2 = new IntOctagon(curr_1.lx, curr_2.ly, curr_2.rx,
Math.max(curr_2.uy, curr_1.uy), curr_1.ulx, curr_2.lrx, curr_1.llx, curr_2.urx);
curr_1 = new IntOctagon(curr_1.lx, curr_1.ly, curr_1.rx, curr_2.ly,
curr_1.ulx, curr_1.lrx, curr_1.llx, curr_1.urx);
result[7] = curr_1.normalize();
result[6] = curr_2.normalize();
}
curr_1 = result[6];
curr_2 = result[5];
if (!(curr_1.is_empty() || curr_2.is_empty()) &&
curr_2.upper_y_value(curr_1.rx) - curr_1.ly
> curr_2.right_x_value(curr_1.ly) - curr_2.lx)
// switch the vertical upper right divide line to horizontal
{
curr_1 = new IntOctagon(curr_1.lx, curr_1.ly, curr_2.rx,
Math.max(curr_2.uy, curr_1.uy), curr_1.ulx, curr_2.lrx, curr_1.llx, curr_2.urx);
curr_2 = new IntOctagon(curr_2.lx, curr_2.ly, curr_2.rx, curr_1.ly,
curr_2.ulx, curr_2.lrx, curr_2.llx, curr_2.urx);
result[6] = curr_1.normalize();
result[5] = curr_2.normalize();
}
curr_1 = result[5];
curr_2 = result[4];
if (!(curr_1.is_empty() || curr_2.is_empty()) &&
curr_2.right_x_value(curr_2.uy) - curr_2.lx
> curr_1.upper_y_value(curr_2.lx) - curr_2.uy)
// switch the horizontal upper right divide line to vertical
{
curr_2 = new IntOctagon(curr_2.lx, curr_2.ly, Math.max(curr_2.rx, curr_1.rx),
curr_1.uy, curr_1.ulx, curr_2.lrx, curr_2.llx, curr_1.urx);
curr_1 = new IntOctagon(curr_1.lx, curr_1.ly, curr_2.lx, curr_1.uy,
curr_1.ulx, curr_1.lrx, curr_1.llx, curr_1.urx);
result[5] = curr_1.normalize();
result[4] = curr_2.normalize();
}
curr_1 = result[4];
curr_2 = result[3];
if (!(curr_1.is_empty() || curr_2.is_empty()) &&
curr_1.right_x_value(curr_1.ly) - curr_1.lx
> curr_1.ly - curr_2.lower_y_value(curr_1.lx))
// switch the horizontal lower right divide line to vertical
{
curr_1 = new IntOctagon(curr_1.lx, curr_2.ly, Math.max(curr_2.rx, curr_1.rx),
curr_1.uy, curr_1.ulx, curr_2.lrx, curr_2.llx, curr_1.urx);
curr_2 = new IntOctagon(curr_2.lx, curr_2.ly, curr_1.lx, curr_2.uy,
curr_2.ulx, curr_2.lrx, curr_2.llx, curr_2.urx);
result[4] = curr_1.normalize();
result[3] = curr_2.normalize();
}
curr_1 = result[3];
curr_2 = result[2];
if (!(curr_1.is_empty() || curr_2.is_empty()) &&
curr_2.uy - curr_2.lower_y_value(curr_2.rx)
> curr_1.right_x_value(curr_2.uy) - curr_2.rx)
// switch the vertical lower right divide line to horizontal
{
curr_2 = new IntOctagon(curr_2.lx, Math.min(curr_1.ly, curr_2.ly),
curr_1.rx, curr_2.uy, curr_2.ulx, curr_1.lrx, curr_2.llx, curr_1.urx);
curr_1 = new IntOctagon(curr_1.lx, curr_2.uy, curr_1.rx, curr_1.uy,
curr_1.ulx, curr_1.lrx, curr_1.llx, curr_1.urx);
result[3] = curr_1.normalize();
result[2] = curr_2.normalize();
}
curr_1 = result[2];
curr_2 = result[1];
if (!(curr_1.is_empty() || curr_2.is_empty()) &&
curr_1.uy - curr_1.lower_y_value(curr_1.lx)
> curr_1.lx - curr_2.left_x_value(curr_1.uy))
// switch the vertical lower left divide line to horizontal
{
curr_1 = new IntOctagon(curr_2.lx, Math.min(curr_1.ly, curr_2.ly),
curr_1.rx, curr_1.uy, curr_2.ulx, curr_1.lrx, curr_2.llx, curr_1.urx);
curr_2 = new IntOctagon(curr_2.lx, curr_1.uy, curr_2.rx, curr_2.uy,
curr_2.ulx, curr_2.lrx, curr_2.llx, curr_2.urx);
result[2] = curr_1.normalize();
result[1] = curr_2.normalize();
}
curr_1 = result[1];
curr_2 = result[0];
if (!(curr_1.is_empty() || curr_2.is_empty()) &&
curr_2.rx - curr_2.left_x_value(curr_2.ly)
> curr_2.ly - curr_1.lower_y_value(curr_2.rx))
// switch the horizontal lower left divide line to vertical
{
curr_2 = new IntOctagon(Math.min(curr_2.lx, curr_1.lx), curr_1.ly,
curr_2.rx, curr_2.uy, curr_2.ulx, curr_1.lrx, curr_1.llx, curr_2.urx);
curr_1 = new IntOctagon(curr_2.rx, curr_1.ly, curr_1.rx, curr_1.uy,
curr_1.ulx, curr_1.lrx, curr_1.llx, curr_1.urx);
result[1] = curr_1.normalize();
result[0] = curr_2.normalize();
}
return result;
}
Simplex[] cutout_from(Simplex p_simplex)
{
return this.to_Simplex().cutout_from(p_simplex);
}
/**
* x coordinate of the left border line
*/
public final int lx;
/**
* y coordinate of the lower border line
*/
public final int ly;
/**
* x coordinate of the right border line
*/
public final int rx;
/**
* y coordinate of the upper border line
*/
public final int uy;
/**
* x axis intersection of the upper left border line
*/
public final int ulx;
/**
* x axis intersection of the lower right border line
*/
public final int lrx;
/**
* x axis intersection of the lower left border line
*/
public final int llx;
/**
* x axis intersection of the upper right border line
*/
public final int urx;
/**
* Result of to_simplex() memorized for performance reasons.
*/
private Simplex precalculated_to_simplex = null;
}