/* * 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. * * Circle.java * * Created on 4. Juni 2003, 07:29 */ package geometry.planar; /** * Discribes functionality of a circle shape in the plane. * * @author Alfons Wirtz */ public class Circle implements ConvexShape, java.io.Serializable { /** Creates a new instance of Circle */ public Circle(IntPoint p_center, int p_radius) { center = p_center; if (p_radius < 0) { System.out.println("Circle: unexpected negative radius"); radius = -p_radius; } else { radius = p_radius; } } public boolean is_empty() { return false; } public boolean is_bounded() { return true; } public int dimension() { if (radius == 0) { // circle is reduced to a point return 0; } return 2; } public double circumference() { return 2.0 * Math.PI * radius; } public double area() { return ( Math.PI * radius) * radius; } public FloatPoint centre_of_gravity() { return center.to_float(); } public boolean is_outside(Point p_point) { FloatPoint fp = p_point.to_float(); return fp.distance_square(center.to_float()) > (double) radius * radius; } public boolean contains(Point p_point) { return !is_outside(p_point); } public boolean contains_inside(Point p_point) { FloatPoint fp = p_point.to_float(); return fp.distance_square(center.to_float()) < (double) radius * radius; } public boolean contains_on_border(Point p_point) { FloatPoint fp = p_point.to_float(); return fp.distance_square(center.to_float()) == (double) radius * radius; } public boolean contains(FloatPoint p_point) { return p_point.distance_square(center.to_float()) <= (double) radius * radius; } public double distance(FloatPoint p_point) { double d = p_point.distance(center.to_float()) - radius; return Math.max(d, 0.0); } public double smallest_radius() { return radius; } public IntBox bounding_box() { int llx = center.x - radius; int urx = center.x + radius; int lly = center.y - radius; int ury = center.y + radius; return new IntBox(llx, lly, urx, ury); } public IntOctagon bounding_octagon() { int lx = center.x - radius; int rx = center.x + radius; int ly = center.y - radius; int uy = center.y + radius; final double sqrt2_minus_1 = Math.sqrt(2) - 1; final int ceil_corner_value = (int) Math.ceil(sqrt2_minus_1 * radius); final int floor_corner_value = (int) Math.floor(sqrt2_minus_1 * radius); int ulx = lx - (center.y + floor_corner_value); int lrx = rx - (center.y - ceil_corner_value); int llx = lx + (center.y - floor_corner_value); int urx = rx + (center.y + ceil_corner_value); return new IntOctagon(lx, ly, rx, uy, ulx, lrx, llx, urx); } public TileShape bounding_tile() { return bounding_octagon(); // the following caused problems with the spring_over algorithm in routing. /* if (this.precalculated_bounding_tile == null) { this.precalculated_bounding_tile = bounding_tile(c_max_approximation_segment_length); } return this.precalculated_bounding_tile; */ } /** * Creates a bounding tile shape around this circle, so that the length of the * line segments of the tile is at most p_max_segment_length. */ public TileShape bounding_tile(int p_max_segment_length) { int quadrant_division_count = this.radius / p_max_segment_length + 1; if (quadrant_division_count <= 2) { return this.bounding_octagon(); } Line [] tangent_line_arr = new Line [quadrant_division_count * 4]; for (int i = 0; i < quadrant_division_count; ++i) { // calculate the tangential points in the first quadrant Vector border_delta; if (i == 0) { border_delta = new IntVector(this.radius, 0); } else { double curr_angle = i * Math.PI / (2.0 * quadrant_division_count); int curr_x = (int) Math.ceil(Math.sin(curr_angle) * this.radius); int curr_y = (int) Math.ceil(Math.cos(curr_angle) * this.radius); border_delta = new IntVector(curr_x, curr_y); } Point curr_a = this.center.translate_by(border_delta); Point curr_b = curr_a.turn_90_degree(1, this.center); Direction curr_dir = Direction.get_instance(curr_b.difference_by(this.center)); Line curr_tangent = new Line(curr_a, curr_dir); tangent_line_arr [quadrant_division_count + i] = curr_tangent; tangent_line_arr [2 * quadrant_division_count + i] = curr_tangent.turn_90_degree(1, this.center); tangent_line_arr [3 * quadrant_division_count + i] = curr_tangent.turn_90_degree(2, this.center); tangent_line_arr [i] = curr_tangent.turn_90_degree(3, this.center); } return TileShape.get_instance(tangent_line_arr); } public boolean is_contained_in(IntBox p_box) { if (p_box.ll.x > center.x - radius) { return false; } if (p_box.ll.y > center.y - radius) { return false; } if (p_box.ur.x < center.x + radius) { return false; } if (p_box.ur.y < center.y + radius) { return false; } return true; } public Circle turn_90_degree(int p_factor, IntPoint p_pole) { IntPoint new_center = (IntPoint) center.turn_90_degree(p_factor, p_pole); return new Circle(new_center, radius); } public Circle rotate_approx(double p_angle, FloatPoint p_pole) { IntPoint new_center = center.to_float().rotate(p_angle, p_pole).round(); return new Circle(new_center, radius); } public Circle mirror_vertical(IntPoint p_pole) { IntPoint new_center = (IntPoint) center.mirror_vertical(p_pole); return new Circle(new_center, radius); } public Circle mirror_horizontal(IntPoint p_pole) { IntPoint new_center = (IntPoint) center.mirror_horizontal(p_pole); return new Circle(new_center, radius); } public double max_width() { return 2 * this.radius; } public double min_width() { return 2 * this.radius; } public RegularTileShape bounding_shape(ShapeBoundingDirections p_dirs) { return p_dirs.bounds(this); } public Circle offset(double p_offset) { double new_radius = this.radius + p_offset; int r = (int) Math.round(new_radius); return new Circle(this.center, r); } public Circle shrink(double p_offset) { double new_radius = this.radius - p_offset; int r = Math.max((int)Math.round(new_radius), 1); return new Circle(this.center, r); } public Circle translate_by(Vector p_vector) { if (p_vector.equals(Vector.ZERO)) { return this; } if (!(p_vector instanceof IntVector)) { System.out.println("Circle.translate_by only implemented for IntVectors till now"); return this; } IntPoint new_center = (IntPoint) center.translate_by(p_vector); return new Circle(new_center, radius); } public FloatPoint nearest_point_approx(FloatPoint p_point) { System.out.println("Circle.nearest_point_approx not yet implemented"); return null; } public double border_distance(FloatPoint p_point) { double d = p_point.distance(center.to_float()) - radius; return Math.abs(d); } public Circle enlarge(double p_offset) { if (p_offset == 0) { return this; } int new_radius = radius + (int)Math.round(p_offset); return new Circle(center, new_radius); } public boolean intersects(Shape p_other) { return p_other.intersects(this); } public Polyline[] cutout(Polyline p_polyline) { System.out.println("Circle.cutout not yet implemented"); return null; } public boolean intersects(Circle p_other) { double d_square = radius + p_other.radius; d_square *= d_square; return center.distance_square(p_other.center) <= d_square; } public boolean intersects(IntBox p_box) { return p_box.distance(center.to_float()) <= radius; } public boolean intersects(IntOctagon p_oct) { return p_oct.distance(center.to_float()) <= radius; } public boolean intersects(Simplex p_simplex) { return p_simplex.distance(center.to_float()) <= radius; } public TileShape[] split_to_convex() { TileShape[] result = new TileShape[1]; result[0] = this.bounding_tile(); return result; } public Circle get_border() { return this; } public Shape[] get_holes() { return new Shape[0]; } public FloatPoint[] corner_approx_arr() { return new FloatPoint[0]; } public String toString() { return to_string(java.util.Locale.ENGLISH); } public String to_string(java.util.Locale p_locale) { String result = "Circle: "; if (!(center.equals(Point.ZERO))) { String center_string = "center " + center.toString(); result += center_string; } java.text.NumberFormat nf = java.text.NumberFormat.getInstance(p_locale); String radius_string = "radius " + nf.format(radius); result += radius_string; return result; } public final IntPoint center; public final int radius; // private TileShape precalculated_bounding_tile = null; // private static final int c_max_approximation_segment_length = 10000; }