/*
* 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;
/**
*
* Abstract class defining functionality for convex shapes, whose
* borders consists of straight lines.
*
* @author Alfons Wirtz
*/
public abstract class TileShape extends PolylineShape implements ConvexShape, java.io.Serializable
{
/**
* creates a Simplex as intersection of the halfplanes defined
* by an array of directed lines
*/
public static TileShape get_instance(Line[] p_line_arr)
{
Simplex result = Simplex.get_instance(p_line_arr);
return result.simplify();
}
/**
* Creates a TileShape from a Point array, who forms the corners of the shape
* of a convex polygon. May work only for IntPoints.
*/
public static TileShape get_instance(Point[] p_convex_polygon)
{
Line[] line_arr = new Line[p_convex_polygon.length];
for (int j = 0; j < line_arr.length - 1; ++j)
{
line_arr[j] = new Line(p_convex_polygon[j], p_convex_polygon[j + 1]);
}
line_arr[line_arr.length - 1] =
new Line(p_convex_polygon[line_arr.length - 1], p_convex_polygon[0]);
return get_instance(line_arr);
}
/**
* creates a half_plane from a directed line
*/
public static TileShape get_instance(Line p_line)
{
Line[] lines = new Line[1];
lines[0] = p_line;
return Simplex.get_instance(lines);
}
/**
* Creates a normalized IntOctagon from the input values.
* For the meaning of the parameter shortcuts see class IntOctagon.
*/
public static IntOctagon get_instance(int p_lx, int p_ly, int p_rx,
int p_uy, int p_ulx, int p_lrx,
int p_llx, int p_urx)
{
IntOctagon oct = new IntOctagon(p_lx, p_ly, p_rx, p_uy, p_ulx,
p_lrx, p_llx, p_urx);
return oct.normalize();
}
/**
* creates a boxlike convex shape
*/
public static IntOctagon get_instance(int p_lower_left_x,
int p_lower_left_y,
int p_upper_right_x,
int p_upper_right_y)
{
IntBox box = new IntBox(p_lower_left_x, p_lower_left_y,
p_upper_right_x, p_upper_right_y);
return box.to_IntOctagon();
}
/**
* creates the smallest IntOctagon containing p_point
*/
public static IntBox get_instance(Point p_point)
{
return p_point.surrounding_box();
}
/**
* Tries to simplify the result shape to a simpler shape.
* Simplifying always in the intersection function may cause performance problems.
*/
public TileShape intersection_with_simplify(TileShape p_other)
{
TileShape result = this.intersection(p_other);
return result.simplify();
}
/**
* Converts the physical instance of this shape to a simpler physical instance, if possible.
*/
public abstract TileShape simplify();
/**
* checks if this TileShape is an IntBox or can be converted into an IntBox
*/
public abstract boolean is_IntBox();
/**
* checks if this TileShape is an IntOctagon or can be converted into an IntOctagon
*/
public abstract boolean is_IntOctagon();
/**
* Returns the intersection of this shape with p_other
*/
public abstract TileShape intersection(TileShape p_other);
/**
* Returns the p_no-th edge line of this shape
* for p_no between 0 and edge_line_count() - 1.
* The edge lines are sorted in counterclock sense around
* the shape starting with the edge with the smallest direction.
*/
public abstract Line border_line(int p_no);
/**
* if p_line is a borderline of this shape the number of that
* edge is returned, otherwise -1
*/
public abstract int border_line_index(Line p_line);
/**
* Converts the internal representation of this TieShape to a Simplex
*/
public abstract Simplex to_Simplex();
/**
* Returns the content of the area of the shape.
* If the shape is unbounded, Double.MAX_VALUE is returned.
*/
public double area()
{
if (!is_bounded())
{
return Double.MAX_VALUE;
}
if (dimension() < 2)
{
return 0;
}
// calculate half of the absolute value of
// x0 (y1 - yn-1) + x1 (y2 - y0) + x2 (y3 - y1) + ...+ xn-1( y0 - yn-2)
// where xi, yi are the coordinates of the i-th corner of this TileShape.
double result = 0;
int corner_count = border_line_count();
FloatPoint prev_corner = corner_approx(corner_count - 2);
FloatPoint curr_corner = corner_approx(corner_count - 1);
for (int i = 0; i < corner_count; ++i)
{
FloatPoint next_corner = corner_approx(i);
result += curr_corner.x * (next_corner.y - prev_corner.y);
prev_corner = curr_corner;
curr_corner = next_corner;
}
result = 0.5 * Math.abs(result);
return result;
}
/**
* Returns true, if p_point is not contained in the inside or the
* edge of the shape
*/
public boolean is_outside(Point p_point)
{
int line_count = border_line_count();
if (line_count == 0)
{
return true;
}
for (int i = 0; i < line_count; ++i)
{
if (border_line(i).side_of(p_point) == Side.ON_THE_LEFT)
{
return true;
}
}
return false;
}
public boolean contains(Point p_point)
{
return !is_outside(p_point);
}
/**
* Returns true, if p_point is contained in this shape,
* but not on an edge line
*/
public boolean contains_inside(Point p_point)
{
int line_count = border_line_count();
if (line_count == 0)
{
return false;
}
for (int i = 0; i < line_count; ++i)
{
if (border_line(i).side_of(p_point) != Side.ON_THE_RIGHT)
{
return false;
}
}
return true;
}
/**
* Returns true, if p_point is contained in this shape.
*/
public boolean contains(FloatPoint p_point)
{
return contains(p_point, 0);
}
/**
* Returns true, if p_point is contained in this shape with tolerance p_tolerance.
* p_tolerance is used when determing, if a point is on the left side of a border line.
* It is used there in calculating a determinant and is not the distance of p_point to the border.
*/
public boolean contains(FloatPoint p_point, double p_tolerance)
{
int line_count = border_line_count();
if (line_count == 0)
{
return false;
}
for (int i = 0; i < line_count; ++i)
{
if (border_line(i).side_of(p_point, p_tolerance) != Side.ON_THE_RIGHT)
{
return false;
}
}
return true;
}
/**
* Returns Side.COLLINEAR if p_point is on the border of this shape with tolerance p_tolerence.
* p_tolerance is used when determing, if a point is on the right side of a border line.
* It is used there in calculating a determinant and is not the distance of p_point to the border.
* Otherwise the function returns Side.ON_THE_LEFT if p_point is outside of this shape,
* and Side.ON_THE_RIGTH if p_point is inside this shape.
*/
public Side side_of_border(FloatPoint p_point, double p_tolerance)
{
int line_count = border_line_count();
if (line_count == 0)
{
return Side.COLLINEAR;
}
Side result = Side.ON_THE_RIGHT; // point is inside
for (int i = 0; i < line_count; ++i)
{
Side curr_side = border_line(i).side_of(p_point, p_tolerance);
if (curr_side == Side.ON_THE_LEFT)
{
return Side.ON_THE_LEFT; // point is outside
}
else if (curr_side == Side.COLLINEAR)
{
result = curr_side;
}
}
return result;
}
/**
* If p_point lies on the border of this shape, the number of the
* edge line segment containing p_point is returned,
* otherwise -1 is returned.
*/
public int contains_on_border_line_no(Point p_point)
{
int line_count = border_line_count();
if (line_count == 0)
{
return -1;
}
int containing_line_no = -1;
for (int i = 0; i < line_count; ++i)
{
Side side_of = border_line(i).side_of(p_point);
if (side_of == Side.ON_THE_LEFT)
{
// p_point outside the convex shape
return -1;
}
if (side_of == Side.COLLINEAR)
{
containing_line_no = i;
}
}
return containing_line_no;
}
/**
* Returns true, if p_point lies exact on the boundary of the shape
*/
public boolean contains_on_border(Point p_point)
{
return (contains_on_border_line_no(p_point) >= 0);
}
/**
* Returns true, if this shape contains p_other completely.
* THere may be some numerical inaccurracy.
*/
public boolean contains_approx(TileShape p_other)
{
FloatPoint[] corners = p_other.corner_approx_arr();
for (FloatPoint curr_corner : corners)
{
if (!this.contains(curr_corner))
{
return false;
}
}
return true;
}
/**
* Returns true, if this shape contains p_other completely.
*/
public boolean contains(TileShape p_other)
{
for (int i = 0; i < p_other.border_line_count(); ++i)
{
if (!this.contains(p_other.corner(i)))
{
return false;
}
}
return true;
}
/**
* Returns the distance between p_point and its nearest point
* on the shape. 0, if p_point is contained in this shape
*/
public double distance(FloatPoint p_point)
{
FloatPoint nearest_point = nearest_point_approx(p_point);
return nearest_point.distance(p_point);
}
/**
* Returns the distance between p_point and its nearest point
* on the edge of the shape.
*/
public double border_distance(FloatPoint p_point)
{
FloatPoint nearest_point = nearest_border_point_approx(p_point);
return nearest_point.distance(p_point);
}
public double smallest_radius()
{
return border_distance(centre_of_gravity());
}
/**
* Returns the point in this shape, which has the smallest
* distance to p_from_point. p_from_point, if that point
* is contained in this shape
*/
public Point nearest_point(Point p_from_point)
{
if (!is_outside(p_from_point))
{
return p_from_point;
}
return nearest_border_point(p_from_point);
}
public FloatPoint nearest_point_approx(FloatPoint p_from_point)
{
if (this.contains(p_from_point))
{
return p_from_point;
}
return nearest_border_point_approx(p_from_point);
}
/**
* Returns a nearest point to p_from_point on the edge of the shape
*/
public Point nearest_border_point(Point p_from_point)
{
int line_count = border_line_count();
if (line_count == 0)
{
return null;
}
FloatPoint from_point_f = p_from_point.to_float();
if (line_count == 1)
{
return border_line(0).perpendicular_projection(p_from_point);
}
Point nearest_point = null;
double min_dist = Double.MAX_VALUE;
int min_dist_ind = 0;
// calculate the distance to the nearest corner first
for (int i = 0; i < line_count; ++i)
{
FloatPoint curr_corner_f = corner_approx(i);
double curr_dist = curr_corner_f.distance_square(from_point_f);
if (curr_dist < min_dist)
{
min_dist = curr_dist;
min_dist_ind = i;
}
}
nearest_point = corner(min_dist_ind);
int prev_ind = line_count - 2;
int curr_ind = line_count - 1;
for (int next_ind = 0; next_ind < line_count; ++next_ind)
{
Point projection =
border_line(curr_ind).perpendicular_projection(p_from_point);
if ((!corner_is_bounded(curr_ind) || border_line(prev_ind).side_of(projection) == Side.ON_THE_RIGHT) && (!corner_is_bounded(next_ind) || border_line(next_ind).side_of(projection) == Side.ON_THE_RIGHT))
{
FloatPoint projection_f = projection.to_float();
double curr_dist = projection_f.distance_square(from_point_f);
if (curr_dist < min_dist)
{
min_dist = curr_dist;
nearest_point = projection;
}
}
prev_ind = curr_ind;
curr_ind = next_ind;
}
return nearest_point;
}
/**
* Returns an approximation of the nearest point
* to p_from_point on the border of the this shape
*/
public FloatPoint nearest_border_point_approx(FloatPoint p_from_point)
{
FloatPoint[] nearest_points = nearest_border_points_approx(p_from_point, 1);
if (nearest_points.length <= 0)
{
return null;
}
return nearest_points[0];
}
/**
* Returns an approximation of the p_count nearest points
* to p_from_point on the border of the this shape.
* The result points must be located on different border lines and are
* sorted in ascending order (the nearest point comes first).
*/
public FloatPoint[] nearest_border_points_approx(FloatPoint p_from_point, int p_count)
{
if (p_count <= 0)
{
return new FloatPoint[0];
}
int line_count = border_line_count();
int result_count = Math.min(p_count, line_count);
if (line_count == 0)
{
return new FloatPoint[0];
}
if (line_count == 1)
{
FloatPoint[] result = new FloatPoint[1];
result[0] = p_from_point.projection_approx(border_line(0));
return result;
}
if (this.dimension() == 0)
{
FloatPoint[] result = new FloatPoint[1];
result[0] = corner_approx(0);
return result;
}
FloatPoint[] nearest_points = new FloatPoint[result_count];
double[] min_dists = new double[result_count];
for (int i = 0; i < result_count; ++i)
{
min_dists[i] = Double.MAX_VALUE;
}
// calculate the distances to the nearest corners first
for (int i = 0; i < line_count; ++i)
{
if (corner_is_bounded(i))
{
FloatPoint curr_corner = corner_approx(i);
double curr_dist = curr_corner.distance_square(p_from_point);
for (int j = 0; j < result_count; ++j)
{
if (curr_dist < min_dists[j])
{
for (int k = j + 1; k < result_count; ++k)
{
min_dists[k] = min_dists[k - 1];
nearest_points[k] = nearest_points[k - 1];
}
min_dists[j] = curr_dist;
nearest_points[j] = curr_corner;
break;
}
}
}
}
int prev_ind = line_count - 2;
int curr_ind = line_count - 1;
for (int next_ind = 0; next_ind < line_count; ++next_ind)
{
FloatPoint projection = p_from_point.projection_approx(border_line(curr_ind));
if ((!corner_is_bounded(curr_ind) || border_line(prev_ind).side_of(projection) == Side.ON_THE_RIGHT) && (!corner_is_bounded(next_ind) || border_line(next_ind).side_of(projection) == Side.ON_THE_RIGHT))
{
double curr_dist = projection.distance_square(p_from_point);
for (int j = 0; j < result_count; ++j)
{
if (curr_dist < min_dists[j])
{
for (int k = j + 1; k < result_count; ++k)
{
min_dists[k] = min_dists[k - 1];
nearest_points[k] = nearest_points[k - 1];
}
min_dists[j] = curr_dist;
nearest_points[j] = projection;
break;
}
}
}
prev_ind = curr_ind;
curr_ind = next_ind;
}
return nearest_points;
}
/**
* Returns the number of a nearest corner of the shape
* to p_from_point
*/
public int index_of_nearest_corner(Point p_from_point)
{
FloatPoint from_point_f = p_from_point.to_float();
int result = 0;
int corner_count = border_line_count();
double min_dist = Double.MIN_VALUE;
for (int i = 0; i < corner_count; ++i)
{
double curr_dist = corner_approx(i).distance(from_point_f);
if (curr_dist < min_dist)
{
min_dist = curr_dist;
result = i;
}
}
return result;
}
/**
* Returns a line segment consisting of an approximations of the corners with
* index 0 and corner_count / 2.
*/
public FloatLine diagonal_corner_segment()
{
if (this.is_empty())
{
return null;
}
FloatPoint first_corner = this.corner_approx(0);
FloatPoint last_corner = this.corner_approx(this.border_line_count() / 2);
return new FloatLine(first_corner, last_corner);
}
/**
* Returns an approximation of the p_count nearest relative outside locations
* of p_shape in the direction of different border lines of this shape.
* These relative locations are sorted in ascending order (the shortest comes first).
*/
public FloatPoint[] nearest_relative_outside_locations(TileShape p_shape, int p_count)
{
int line_count = border_line_count();
if (p_count <= 0 || line_count < 3 || !this.intersects(p_shape))
{
return new FloatPoint[0];
}
int result_count = Math.min(p_count, line_count);
FloatPoint[] translate_coors = new FloatPoint[result_count];
double[] min_dists = new double[result_count];
for (int i = 0; i < result_count; ++i)
{
min_dists[i] = Double.MAX_VALUE;
}
int curr_ind = line_count - 1;
int other_line_count = p_shape.border_line_count();
for (int next_ind = 0; next_ind < line_count; ++next_ind)
{
double curr_max_dist = 0;
FloatPoint curr_translate_coor = FloatPoint.ZERO;
for (int corner_no = 0; corner_no < other_line_count; ++corner_no)
{
FloatPoint curr_corner = p_shape.corner_approx(corner_no);
if (border_line(curr_ind).side_of(curr_corner) == Side.ON_THE_RIGHT)
{
FloatPoint projection = curr_corner.projection_approx(border_line(curr_ind));
double curr_dist = projection.distance_square(curr_corner);
if (curr_dist > curr_max_dist)
{
curr_max_dist = curr_dist;
curr_translate_coor = projection.substract(curr_corner);
}
}
}
for (int j = 0; j < result_count; ++j)
{
if (curr_max_dist < min_dists[j])
{
for (int k = j + 1; k < result_count; ++k)
{
min_dists[k] = min_dists[k - 1];
translate_coors[k] = translate_coors[k - 1];
}
min_dists[j] = curr_max_dist;
translate_coors[j] = curr_translate_coor;
break;
}
}
curr_ind = next_ind;
}
return translate_coors;
}
public ConvexShape shrink(double p_offset)
{
ConvexShape result = this.offset(-p_offset);
if (result.is_empty())
{
IntBox centre_box = this.centre_of_gravity().bounding_box();
result = this.intersection(centre_box);
}
return result;
}
/**
* Returns the maximum of the edge widths of the shape.
* Only defined when the shape is bounded.
*/
public double length()
{
if (!this.is_bounded())
{
return Integer.MAX_VALUE;
}
int dimension = this.dimension();
if (dimension <= 0)
{
return 0;
}
if (dimension == 1)
{
return this.circumference() / 2;
}
// now the shape is 2-dimensional
double max_distance = -1;
double max_distance_2 = -1;
FloatPoint gravity_point = this.centre_of_gravity();
for (int i = 0; i < border_line_count(); ++i)
{
double curr_distance = Math.abs(border_line(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;
}
/**
* Calculates, if this Shape and p_other habe a common border piece and returns
* an 2 dimensional array with the indices in this shape and p_other of the
* touching edge lines in this case.
* Otherwise an array of dimension 0 is returned.
* Used if the intersection shape is 1-dimensional.
*/
public int[] touching_sides(TileShape p_other)
{
// search the first edge line of p_other with reverse direction >= right
int side_no_2 = -1;
Direction dir2 = null;
for (int i = 0; i < p_other.border_line_count(); ++i)
{
Direction curr_dir = p_other.border_line(i).direction();
if (curr_dir.compareTo(Direction.LEFT) >= 0)
{
side_no_2 = i;
dir2 = curr_dir.opposite();
break;
}
}
if (dir2 == null)
{
System.out.println("touching_side : dir2 not found");
return new int[0];
}
int side_no_1 = 0;
Direction dir1 = this.border_line(0).direction();
final int max_ind = this.border_line_count() + p_other.border_line_count();
for (int i = 0; i < max_ind; ++i)
{
int compare = dir2.compareTo(dir1);
if (compare == 0)
{
if (this.border_line(side_no_1).is_equal_or_opposite(p_other.border_line(side_no_2)))
{
int[] result = new int[2];
result[0] = side_no_1;
result[1] = side_no_2;
return result;
}
}
if (compare >= 0) // dir2 is bigger than dir1
{
side_no_1 = (side_no_1 + 1) % this.border_line_count();
dir1 = this.border_line(side_no_1).direction();
}
else //dir1 is bigger than dir2
{
side_no_2 = (side_no_2 + 1) % p_other.border_line_count();
dir2 = p_other.border_line(side_no_2).direction().opposite();
}
}
return new int[0];
}
/**
* Calculates the minimal distance of p_line to this shape,
* assuming, that p_line is on the left of this shape.
* Returns -1, if p_line is on the right of this shape or intersects
* with the interiour of this shape.
*/
public double distance_to_the_left(Line p_line)
{
double result = Integer.MAX_VALUE;
for (int i = 0; i < this.border_line_count(); ++i)
{
FloatPoint curr_corner = this.corner_approx(i);
Side line_side = p_line.side_of(curr_corner, 1);
if (line_side == Side.COLLINEAR)
{
line_side = p_line.side_of(this.corner(i));
}
if (line_side == Side.ON_THE_RIGHT)
{
// curr_point would be outside the result shape
result = -1;
break;
}
result = Math.min(result, p_line.signed_distance(curr_corner));
}
return result;
}
/**
* Returns Side.COLLINEAR, if p_line intersects with the interiour of this shape,
* Side.ON_THE_LEFT, if this shape is completely on the left of p_line
* or Side.ON_THE_RIGHT, if this shape is completely on the right of p_line.
*/
public Side side_of(Line p_line)
{
boolean on_the_left = false;
boolean on_the_right = false;
for (int i = 0; i < this.border_line_count(); ++i)
{
Side curr_side = p_line.side_of(this.corner(i));
if (curr_side == Side.ON_THE_LEFT)
{
on_the_right = true;
}
else if (curr_side == Side.ON_THE_RIGHT)
{
on_the_left = true;
}
if (on_the_left && on_the_right)
{
return Side.COLLINEAR;
}
}
Side result;
if (on_the_left)
{
result = Side.ON_THE_LEFT;
}
else
{
result = Side.ON_THE_RIGHT;
}
return result;
}
public TileShape turn_90_degree(int p_factor, IntPoint p_pole)
{
Line[] new_lines = new Line[border_line_count()];
for (int i = 0; i < new_lines.length; ++i)
{
new_lines[i] = this.border_line(i).turn_90_degree(p_factor, p_pole);
}
return get_instance(new_lines);
}
public TileShape rotate_approx(double p_angle, FloatPoint p_pole)
{
if (p_angle == 0)
{
return this;
}
IntPoint[] new_corners = new IntPoint[border_line_count()];
for (int i = 0; i < new_corners.length; ++i)
{
new_corners[i] = this.corner_approx(i).rotate(p_angle, p_pole).round();
}
Polygon corner_polygon = new Polygon(new_corners);
Point[] polygon_corners = corner_polygon.corner_array();
TileShape result;
if (polygon_corners.length >= 3)
{
result = get_instance(polygon_corners);
}
else if (polygon_corners.length == 2)
{
Polyline curr_polyline = new Polyline(polygon_corners);
LineSegment curr_segment = new LineSegment(curr_polyline, 0);
result = curr_segment.to_simplex();
}
else if (polygon_corners.length == 1)
{
result = get_instance(polygon_corners[0]);
}
else
{
result = Simplex.EMPTY;
}
return result;
}
public TileShape mirror_vertical(IntPoint p_pole)
{
Line[] new_lines = new Line[border_line_count()];
for (int i = 0; i < new_lines.length; ++i)
{
new_lines[i] = this.border_line(i).mirror_vertical(p_pole);
}
return get_instance(new_lines);
}
public TileShape mirror_horizontal(IntPoint p_pole)
{
Line[] new_lines = new Line[border_line_count()];
for (int i = 0; i < new_lines.length; ++i)
{
new_lines[i] = this.border_line(i).mirror_horizontal(p_pole);
}
return get_instance(new_lines);
}
/**
* Calculates the border line of this shape intersecting the ray from p_from_point into the direction p_direction.
* p_from_point is assumed to be inside this shape, otherwise -1 is returned.
*/
public int intersecting_border_line_no(Point p_from_point, Direction p_direction)
{
if (!this.contains(p_from_point))
{
return -1;
}
FloatPoint from_point = p_from_point.to_float();
Line intersection_line = new Line(p_from_point, p_direction);
FloatPoint second_line_point = intersection_line.b.to_float();
int result = -1;
double min_distance = Float.MAX_VALUE;
for (int i = 0; i < this.border_line_count(); ++i)
{
Line curr_border_line = this.border_line(i);
FloatPoint curr_intersection = curr_border_line.intersection_approx(intersection_line);
if (curr_intersection.x >= Integer.MAX_VALUE)
{
continue; // lines are parallel
}
double curr_distence = curr_intersection.distance_square(from_point);
if (curr_distence < min_distance)
{
boolean direction_ok = curr_border_line.side_of(second_line_point) == Side.ON_THE_LEFT || second_line_point.distance_square(curr_intersection) < curr_distence;
if (direction_ok)
{
result = i;
min_distance = curr_distence;
}
}
}
return result;
}
/**
* Cuts p_shape out of this shape and divides the result into convex pieces
*/
public abstract TileShape[] cutout(TileShape p_shape);
/**
* Returns an arry of tuples of integers. The length of the array is
* the number of points, where p_polyline enters or leaves the interiour
* of this shape. The first coordinate of the tuple is the number of
* the line segment of p_polyline, which enters the simplex and the
* second coordinate of the tuple is the number of the edge_line of the
* simplex, which is crossed there.
* That means that the entrance point is the intersection of this 2 lines.
*/
public int[][] entrance_points(Polyline p_polyline)
{
int[][] result = new int[2 * p_polyline.arr.length][2];
int intersection_count = 0;
int prev_intersection_line_no = -1;
int prev_intersection_edge_no = -1;
for (int line_no = 1; line_no < p_polyline.arr.length - 1; ++line_no)
{
LineSegment curr_line_seg = new LineSegment(p_polyline, line_no);
int[] curr_intersections = curr_line_seg.border_intersections(this);
for (int i = 0; i < curr_intersections.length; ++i)
{
int edge_no = curr_intersections[i];
if (line_no != prev_intersection_line_no ||
edge_no != prev_intersection_edge_no)
{
result[intersection_count][0] = line_no;
result[intersection_count][1] = edge_no;
++intersection_count;
prev_intersection_line_no = line_no;
prev_intersection_edge_no = edge_no;
}
}
}
int[][] normalized_result = new int[intersection_count][2];
for (int j = 0; j < intersection_count; ++j)
{
for (int i = 0; i < 2; ++i)
{
normalized_result[j][i] = result[j][i];
}
}
return normalized_result;
}
/**
* Cuts out the parts of p_polyline in the interiour of this shape
* and returns a list of the remaining pieces of p_polyline.
* Pieces completely contained in the border of this shape
* are not returned.
*/
public Polyline[] cutout(Polyline p_polyline)
{
int[][] intersection_no = this.entrance_points(p_polyline);
Point first_corner = p_polyline.first_corner();
boolean first_corner_is_inside = this.contains_inside(first_corner);
if (intersection_no.length == 0)
// no intersections
{
if (first_corner_is_inside)
// p_polyline is contained completely in this shape
{
return new Polyline[0];
}
// p_polyline is completely outside
Polyline[] result = new Polyline[1];
result[0] = p_polyline;
return result;
}
Collection<Polyline> pieces = new LinkedList<Polyline>();
int curr_intersection_no = 0;
int[] curr_intersection_tuple = intersection_no[curr_intersection_no];
Point first_intersection =
p_polyline.arr[curr_intersection_tuple[0]].intersection(this.border_line(curr_intersection_tuple[1]));
if (!first_corner_is_inside)
// calculate outside piece at start
{
if (!first_corner.equals(first_intersection))
// otherwise skip 1 point outside polyline at the start
{
int curr_polyline_intersection_no = curr_intersection_tuple[0];
Line[] curr_lines = new Line[curr_polyline_intersection_no + 2];
System.arraycopy(p_polyline.arr, 0, curr_lines, 0, curr_polyline_intersection_no + 1);
// close the polyline piece with the intersected edge line.
curr_lines[curr_polyline_intersection_no + 1] = this.border_line(curr_intersection_tuple[1]);
Polyline curr_piece = new Polyline(curr_lines);
if (!curr_piece.is_empty())
{
pieces.add(curr_piece);
}
}
++curr_intersection_no;
}
while (curr_intersection_no < intersection_no.length - 1)
// calculate the next outside polyline piece
{
curr_intersection_tuple = intersection_no[curr_intersection_no];
int[] next_intersection_tuple = intersection_no[curr_intersection_no + 1];
int curr_intersection_no_of_polyline = curr_intersection_tuple[0];
int next_intersection_no_of_polyline = next_intersection_tuple[0];
// check that at least 1 corner of p_polyline with number between
// between curr_intersection_no_of_polyline and
// next_intersection_no_of_polyline
// is not contained in this shape. Otherwise the part of p_polyline
// between this intersections is completely contained in the border
// and can be ignored
boolean insert_piece = false;
for (int i = curr_intersection_no_of_polyline + 1;
i < next_intersection_no_of_polyline; ++i)
{
if (this.is_outside(p_polyline.corner(i)))
{
insert_piece = true;
break;
}
}
if (insert_piece)
{
Line[] curr_lines = new Line[next_intersection_no_of_polyline - curr_intersection_no_of_polyline + 3];
curr_lines[0] = this.border_line(curr_intersection_tuple[1]);
System.arraycopy(p_polyline.arr, curr_intersection_no_of_polyline, curr_lines,
1, curr_lines.length - 2);
curr_lines[curr_lines.length - 1] = this.border_line(next_intersection_tuple[1]);
Polyline curr_piece = new Polyline(curr_lines);
if (!curr_piece.is_empty())
{
pieces.add(curr_piece);
}
}
curr_intersection_no += 2;
}
if (curr_intersection_no <= intersection_no.length - 1)
// calculate outside piece at end
{
curr_intersection_tuple = intersection_no[curr_intersection_no];
int curr_polyline_intersection_no = curr_intersection_tuple[0];
Line[] curr_lines = new Line[p_polyline.arr.length - curr_polyline_intersection_no + 1];
curr_lines[0] = this.border_line(curr_intersection_tuple[1]);
System.arraycopy(p_polyline.arr, curr_polyline_intersection_no, curr_lines,
1, curr_lines.length - 1);
Polyline curr_piece = new Polyline(curr_lines);
if (!curr_piece.is_empty())
{
pieces.add(curr_piece);
}
}
Polyline[] result = new Polyline[pieces.size()];
Iterator<Polyline> it = pieces.iterator();
for (int i = 0; i < result.length; ++i)
{
result[i] = it.next();
}
return result;
}
public TileShape[] split_to_convex()
{
TileShape[] result = new TileShape[1];
result[0] = this;
return result;
}
/**
* Divides this shape into sections with width and height at most p_max_section_width
* of about equal size.
*/
public TileShape[] divide_into_sections(double p_max_section_width)
{
if (this.is_empty())
{
TileShape[] result = new TileShape[1];
result[0] = this;
return result;
}
TileShape[] section_boxes = this.bounding_box().divide_into_sections(p_max_section_width);
Collection<TileShape> section_list = new LinkedList<TileShape>();
for (int i = 0; i < section_boxes.length; ++i)
{
TileShape curr_section = this.intersection_with_simplify(section_boxes[i]);
if (curr_section.dimension() == 2)
{
section_list.add(curr_section);
}
}
TileShape[] result = new TileShape[section_list.size()];
Iterator<TileShape> it = section_list.iterator();
for (int i = 0; i < result.length; ++i)
{
result[i] = it.next();
}
return result;
}
/**
* Checks, if p_line_segment has a common point with the interiour of this shape.
*/
public boolean is_intersected_interiour_by(LineSegment p_line_segment)
{
FloatPoint float_start_point = p_line_segment.start_point_approx();
FloatPoint float_end_point = p_line_segment.end_point_approx();
Side[] border_line_side_of_start_point_arr = new Side[this.border_line_count()];
Side[] border_line_side_of_end_point_arr = new Side[border_line_side_of_start_point_arr.length];
for (int i = 0; i < border_line_side_of_start_point_arr.length; ++i)
{
Line curr_border_line = this.border_line(i);
Side border_line_side_of_start_point = curr_border_line.side_of(float_start_point, 1);
if (border_line_side_of_start_point == Side.COLLINEAR)
{
border_line_side_of_start_point = curr_border_line.side_of(p_line_segment.start_point());
}
Side border_line_side_of_end_point = curr_border_line.side_of(float_end_point, 1);
if (border_line_side_of_end_point == Side.COLLINEAR)
{
border_line_side_of_end_point = curr_border_line.side_of(p_line_segment.end_point());
}
if (border_line_side_of_start_point != Side.ON_THE_RIGHT && border_line_side_of_end_point != Side.ON_THE_RIGHT)
{
// both endpoints are outside the border_line,
// no intersection possible
return false;
}
border_line_side_of_start_point_arr[i] = border_line_side_of_start_point;
border_line_side_of_end_point_arr[i] = border_line_side_of_end_point;
}
boolean start_point_is_inside = true;
for (int i = 0; i < border_line_side_of_start_point_arr.length; ++i)
{
if (border_line_side_of_start_point_arr[i] != Side.ON_THE_RIGHT)
{
start_point_is_inside = false;
break;
}
}
if (start_point_is_inside)
{
return true;
}
boolean end_point_is_inside = true;
for (int i = 0; i < border_line_side_of_end_point_arr.length; ++i)
{
if (border_line_side_of_end_point_arr[i] != Side.ON_THE_RIGHT)
{
end_point_is_inside = false;
break;
}
}
if (end_point_is_inside)
{
return true;
}
Line segment_line = p_line_segment.get_line();
// Check, if this line segments intersect a border line of p_shape.
for (int i = 0; i < border_line_side_of_start_point_arr.length; ++i)
{
Side border_line_side_of_start_point = border_line_side_of_start_point_arr[i];
Side border_line_side_of_end_point = border_line_side_of_end_point_arr[i];
if (border_line_side_of_start_point != border_line_side_of_end_point)
{
if (border_line_side_of_start_point == Side.COLLINEAR && border_line_side_of_end_point == Side.ON_THE_LEFT || border_line_side_of_end_point == Side.COLLINEAR && border_line_side_of_start_point == Side.ON_THE_LEFT)
{
// the interiour of p_shape is not intersected.
continue;
}
Side prev_corner_side = segment_line.side_of(this.corner_approx(i), 1);
if (prev_corner_side == Side.COLLINEAR)
{
prev_corner_side = segment_line.side_of(this.corner(i));
}
int next_corner_index;
if (i == border_line_side_of_start_point_arr.length - 1)
{
next_corner_index = 0;
}
else
{
next_corner_index = i + 1;
}
Side next_corner_side = segment_line.side_of(this.corner_approx(next_corner_index), 1);
if (next_corner_side == Side.COLLINEAR)
{
next_corner_side = segment_line.side_of(this.corner(next_corner_index));
}
if (prev_corner_side == Side.ON_THE_LEFT && next_corner_side == Side.ON_THE_RIGHT || prev_corner_side == Side.ON_THE_RIGHT && next_corner_side == Side.ON_THE_LEFT)
{
// this line segment crosses a border line of p_shape
return true;
}
}
}
return false;
}
// auxiliary functions needed because the virtual function mechanism does
// not work in parameter position
abstract TileShape intersection(Simplex p_other);
abstract TileShape intersection(IntOctagon p_other);
abstract TileShape intersection(IntBox p_other);
/**
* Auxiliary function to implement the public function cutout(TileShape p_shape)
*/
abstract TileShape[] cutout_from(IntBox p_shape);
/**
* Auxiliary function to implement the public function cutout(TileShape p_shape)
*/
abstract TileShape[] cutout_from(IntOctagon p_shape);
/**
* Auxiliary function to implement the public function cutout(TileShape p_shape)
*/
abstract TileShape[] cutout_from(Simplex p_shape);
}