/*
* 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.
*
* PolylineArea.java
*
* Created on 19. Juni 2003, 07:58
*/
package geometry.planar;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
/**
* A PolylineArea is an Area, where the outside border curve and the hole borders
* consist of straight lines.
*
* @author Alfons Wirtz
*/
public class PolylineArea implements Area, java.io.Serializable
{
/** Creates a new instance of PolylineShapeWithHoles */
public PolylineArea(PolylineShape p_border_shape, PolylineShape[] p_hole_arr)
{
border_shape = p_border_shape;
hole_arr = p_hole_arr;
}
public int dimension()
{
return border_shape.dimension();
}
public boolean is_bounded()
{
return border_shape.is_bounded();
}
public boolean is_empty()
{
return border_shape.is_empty();
}
public boolean is_contained_in(IntBox p_box)
{
return border_shape.is_contained_in(p_box);
}
public PolylineShape get_border()
{
return border_shape;
}
public PolylineShape[] get_holes()
{
return hole_arr;
}
public IntBox bounding_box()
{
return border_shape.bounding_box();
}
public IntOctagon bounding_octagon()
{
return border_shape.bounding_octagon();
}
public boolean contains(FloatPoint p_point)
{
if (!border_shape.contains(p_point))
{
return false;
}
for (int i = 0; i < hole_arr.length; ++i)
{
if (hole_arr[i].contains(p_point))
{
return false;
}
}
return true;
}
public boolean contains(Point p_point)
{
if (!border_shape.contains(p_point))
{
return false;
}
for (int i = 0; i < hole_arr.length; ++i)
{
if (hole_arr[i].contains_inside(p_point))
{
return false;
}
}
return true;
}
public FloatPoint nearest_point_approx(FloatPoint p_from_point)
{
double min_dist = Double.MAX_VALUE;
FloatPoint result = null;
TileShape[] convex_shapes = split_to_convex();
for (int i = 0; i < convex_shapes.length; ++i)
{
FloatPoint curr_nearest_point = convex_shapes[i].nearest_point_approx(p_from_point);
double curr_dist = curr_nearest_point.distance_square(p_from_point);
if (curr_dist < min_dist)
{
min_dist = curr_dist;
result = curr_nearest_point;
}
}
return result;
}
public PolylineArea translate_by(Vector p_vector)
{
if (p_vector.equals(Vector.ZERO))
{
return this;
}
PolylineShape translated_border = border_shape.translate_by(p_vector);
PolylineShape[] translated_holes = new PolylineShape[hole_arr.length];
for (int i = 0; i < hole_arr.length; ++i)
{
translated_holes[i] = hole_arr[i].translate_by(p_vector);
}
return new PolylineArea(translated_border, translated_holes);
}
public FloatPoint[] corner_approx_arr()
{
int corner_count = border_shape.border_line_count();
for (int i = 0; i < hole_arr.length; ++i)
{
corner_count += hole_arr[i].border_line_count();
}
FloatPoint[] result = new FloatPoint[corner_count];
FloatPoint[] curr_corner_arr = border_shape.corner_approx_arr();
System.arraycopy(curr_corner_arr, 0, result, 0, curr_corner_arr.length);
int dest_pos = curr_corner_arr.length;
for (int i = 0; i < hole_arr.length; ++i)
{
curr_corner_arr = hole_arr[i].corner_approx_arr();
System.arraycopy(curr_corner_arr, 0, result, dest_pos, curr_corner_arr.length);
dest_pos += curr_corner_arr.length;
}
return result;
}
/**
* Splits this polygon shape with holes into convex pieces.
* The result is not exact, because rounded intersections of lines are
* used in the result pieces. It can be made exact, if Polylines are returned
* instead of Polygons, so that no intersection points are needed in the result.
*/
public TileShape[] split_to_convex()
{
return split_to_convex(null);
}
/**
* Splits this polygon shape with holes into convex pieces.
* The result is not exact, because rounded intersections of lines are
* used in the result pieces. It can be made exact, if Polylines are returned
* instead of Polygons, so that no intersection points are needed in the result.
* If p_stoppable_thread != null, this function can be interrupted.
*/
public TileShape[] split_to_convex(datastructures.Stoppable p_stoppable_thread)
{
if (precalculated_convex_pieces == null)
{
TileShape[] convex_border_pieces = border_shape.split_to_convex();
if (convex_border_pieces == null)
{
// split failed
return null;
}
Collection<TileShape> curr_piece_list = new LinkedList<TileShape>();
for (int i = 0; i < convex_border_pieces.length; ++i)
{
curr_piece_list.add(convex_border_pieces[i]);
}
for (int i = 0; i < hole_arr.length; ++i)
{
if (hole_arr[i].dimension() < 2)
{
System.out.println("PolylineArea. split_to_convex: dimennsion 2 for hole expected");
continue;
}
TileShape[] convex_hole_pieces = hole_arr[i].split_to_convex();
if (convex_hole_pieces == null)
{
return null;
}
for (int j = 0; j < convex_hole_pieces.length; ++j)
{
TileShape curr_hole_piece = convex_hole_pieces[j];
Collection<TileShape> new_piece_list = new LinkedList<TileShape>();
Iterator<TileShape> it = curr_piece_list.iterator();
while (it.hasNext())
{
if (p_stoppable_thread != null && p_stoppable_thread.is_stop_requested())
{
return null;
}
TileShape curr_divide_piece = it.next();
cutout_hole_piece(curr_divide_piece, curr_hole_piece, new_piece_list);
}
curr_piece_list = new_piece_list;
}
}
precalculated_convex_pieces = new TileShape[curr_piece_list.size()];
Iterator<TileShape> it = curr_piece_list.iterator();
for (int i = 0; i < precalculated_convex_pieces.length; ++i)
{
precalculated_convex_pieces[i] = it.next();
}
}
return precalculated_convex_pieces;
}
public PolylineArea turn_90_degree(int p_factor, IntPoint p_pole)
{
PolylineShape new_border = border_shape.turn_90_degree(p_factor, p_pole);
PolylineShape[] new_hole_arr = new PolylineShape[hole_arr.length];
for (int i = 0; i < new_hole_arr.length; ++i)
{
new_hole_arr[i] = hole_arr[i].turn_90_degree(p_factor, p_pole);
}
return new PolylineArea(new_border, new_hole_arr);
}
public PolylineArea rotate_approx(double p_angle, FloatPoint p_pole)
{
PolylineShape new_border = border_shape.rotate_approx(p_angle, p_pole);
PolylineShape[] new_hole_arr = new PolylineShape[hole_arr.length];
for (int i = 0; i < new_hole_arr.length; ++i)
{
new_hole_arr[i] = hole_arr[i].rotate_approx(p_angle, p_pole);
}
return new PolylineArea(new_border, new_hole_arr);
}
public PolylineArea mirror_vertical(IntPoint p_pole)
{
PolylineShape new_border = border_shape.mirror_vertical(p_pole);
PolylineShape[] new_hole_arr = new PolylineShape[hole_arr.length];
for (int i = 0; i < new_hole_arr.length; ++i)
{
new_hole_arr[i] = hole_arr[i].mirror_vertical(p_pole);
}
return new PolylineArea(new_border, new_hole_arr);
}
public PolylineArea mirror_horizontal(IntPoint p_pole)
{
PolylineShape new_border = border_shape.mirror_horizontal(p_pole);
PolylineShape[] new_hole_arr = new PolylineShape[hole_arr.length];
for (int i = 0; i < new_hole_arr.length; ++i)
{
new_hole_arr[i] = hole_arr[i].mirror_horizontal(p_pole);
}
return new PolylineArea(new_border, new_hole_arr);
}
static private void cutout_hole_piece(TileShape p_divide_piece, TileShape p_hole_piece,
Collection<TileShape> p_result_pieces)
{
TileShape[] result_pieces = p_divide_piece.cutout(p_hole_piece);
for (int i = 0; i < result_pieces.length; ++i)
{
TileShape curr_piece = result_pieces[i];
if (curr_piece.dimension() == 2)
{
p_result_pieces.add(curr_piece);
}
}
}
final PolylineShape border_shape;
final PolylineShape[] hole_arr;
transient private TileShape[] precalculated_convex_pieces = null;
}