/* * 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. * * FloatPoint.java * * Created on 2. Februar 2003, 09:14 */ package geometry.planar; /** * * Implements a point in the plane as a touple of double's. * Because arithmetic calculations with double's are in general not * exact, FloatPoint is not derived from the abstract class Point. * * * @author Alfons Wirtz */ public class FloatPoint implements java.io.Serializable { public static final FloatPoint ZERO = new FloatPoint(0,0); /** * creates an instance of class FloatPoint from two double's, */ public FloatPoint(double p_x, double p_y) { x = p_x; y = p_y; } public FloatPoint(IntPoint p_pt) { x = p_pt.x ; y = p_pt.y ; } /** * returns the square of the distance from this point to the zero point */ public final double size_square() { return x * x + y * y; } /** * returns the distance from this point to the zero point */ public final double size() { return Math.sqrt(size_square()); } /** * returns the square of the distance from this Point to the Point p_other */ public final double distance_square(FloatPoint p_other) { double dx = p_other.x - x; double dy = p_other.y - y; return dx * dx + dy * dy; } /** * returns the distance from this point to the point p_other */ public final double distance(FloatPoint p_other) { return Math.sqrt(distance_square(p_other)); } /** * Computes the weighted distance to p_other. */ public double weighted_distance(FloatPoint p_other, double p_horizontal_weight, double p_vertical_weight) { double delta_x = this.x - p_other.x; double delta_y = this.y - p_other.y; delta_x *= p_horizontal_weight; delta_y *= p_vertical_weight; double result = Math.sqrt(delta_x * delta_x + delta_y * delta_y); return result; } /** * rounds the coordinates from an object of class Point_double to * an object of class IntPoint */ public IntPoint round() { return new IntPoint((int)Math.round(x), (int)Math.round(y)); } /** * Rounds this point, so that if this point is on the right side * of any directed line with direction p_dir, the result * point will also be on the right side. */ public IntPoint round_to_the_right(Direction p_dir) { FloatPoint dir = p_dir.get_vector().to_float(); int rounded_x; if (dir.y > 0) { rounded_x = (int) Math.ceil(x); } else if (dir.y < 0) { rounded_x = (int) Math.floor(x); } else { rounded_x = (int) Math.round(x); } int rounded_y; if (dir.x > 0) { rounded_y = (int) Math.floor(y); } else if (dir.x < 0) { rounded_y = (int) Math.ceil(y); } else { rounded_y = (int) Math.round(y); } return new IntPoint(rounded_x, rounded_y); } /** * Round this Point so the x coordinate of the result will be a multiple of p_horizontal_grid * and the y coordinate a multiple of p_vertical_grid. */ public IntPoint round_to_grid(int p_horizontal_grid, int p_vertical_grid) { double rounded_x; if (p_horizontal_grid > 0) { rounded_x = Math.rint(this.x / p_horizontal_grid) * p_horizontal_grid; } else { rounded_x = this.x; } double rounded_y; if (p_vertical_grid > 0) { rounded_y = Math.rint(this.y / p_vertical_grid) * p_vertical_grid; } else { rounded_y = this.y; } return new IntPoint((int) rounded_x, (int) rounded_y ); } /** * Rounds this point, so that if this point is on the left side * of any directed line with direction p_dir, the result * point will also be on the left side. */ public IntPoint round_to_the_left(Direction p_dir) { FloatPoint dir = p_dir.get_vector().to_float(); int rounded_x; if (dir.y > 0) { rounded_x = (int) Math.floor(x); } else if (dir.y < 0) { rounded_x = (int) Math.ceil(x); } else { rounded_x = (int) Math.round(x); } int rounded_y; if (dir.x > 0) { rounded_y = (int) Math.ceil(y); } else if (dir.x < 0) { rounded_y = (int) Math.floor(y); } else { rounded_y = (int) Math.round(y); } return new IntPoint(rounded_x, rounded_y); } /** * Adds the coordinates of this FloatPoint and p_other. */ public FloatPoint add(FloatPoint p_other) { return new FloatPoint(this.x + p_other.x, this.y + p_other.y); } /** * Substracts the coordinates of p_other from this FloatPoint. */ public FloatPoint substract(FloatPoint p_other) { return new FloatPoint(this.x - p_other.x, this.y - p_other.y); } /** * Returns an approximation of the perpendicular projection * of this point onto p_line */ public FloatPoint projection_approx(Line p_line) { FloatLine line = new FloatLine(p_line.a.to_float(), p_line.b.to_float()); return line.perpendicular_projection(this); } /** * Calculates the scalar prodct of (p_1 - this). with (p_2 - this). */ public double scalar_product(FloatPoint p_1, FloatPoint p_2) { if (p_1 == null || p_2 == null) { System.out.println("FloatPoint.scalar_product: parameter point is null"); return 0; } double dx_1 = p_1.x - this.x; double dx_2 = p_2.x - this.x; double dy_1 = p_1.y - this.y; double dy_2 = p_2.y - this.y; return (dx_1 * dx_2 + dy_1 * dy_2); } /** * Approximates a FloatPoint on the line from zero to this point * with distance p_new_length from zero. */ public FloatPoint change_size(double p_new_size) { if (x == 0 && y == 0) { // the size of the zero point cannot be changed return this; } double length = Math.sqrt(x * x + y * y); double new_x = (x * p_new_size) / length; double new_y = (y * p_new_size) / length; return new FloatPoint(new_x, new_y); } /** * Approximates a FloatPoint on the line from this point to p_to_point * with distance p_new_length from this point. */ public FloatPoint change_length(FloatPoint p_to_point, double p_new_length) { double dx = p_to_point.x - this.x; double dy = p_to_point.y - this.y; if (dx == 0 && dy == 0) { System.out.println("IntPoint.change_length: Points are equal"); return p_to_point; } double length = Math.sqrt(dx * dx + dy * dy); double new_x = this.x + (dx * p_new_length) / length; double new_y = this.y + (dy * p_new_length) / length; return new FloatPoint(new_x, new_y); } /** * Returns the middle point between this point and p_to_point. */ public FloatPoint middle_point(FloatPoint p_to_point) { if (p_to_point == this) { return this; } double middle_x = 0.5 * (this.x + p_to_point.x); double middle_y = 0.5 * (this.y + p_to_point.y); return new FloatPoint(middle_x, middle_y); } /** * The function returns * Side.ON_THE_LEFT, if this Point is on the left of the line from p_1 to p_2; * and Side.ON_THE_RIGHT, if this Point is on the right of the line from p_1 to p_2. * Collinearity is not defined, becouse numerical calculations ar not exact for FloatPoints. */ public Side side_of(FloatPoint p_1, FloatPoint p_2) { double d21_x = p_2.x - p_1.x; double d21_y = p_2.y - p_1.y; double d01_x = this.x - p_1.x; double d01_y = this.y - p_1.y; double determinant = d21_x * d01_y - d21_y * d01_x; return Side.of(determinant); } /** * Rotates this FloatPoints by p_angle ( in radian ) around the p_pole. */ public FloatPoint rotate(double p_angle, FloatPoint p_pole) { if (p_angle == 0) { return this; } double dx = x - p_pole.x; double dy = y - p_pole.y; double sin_angle = Math.sin(p_angle); double cos_angle = Math.cos(p_angle); double new_dx = dx * cos_angle - dy * sin_angle; double new_dy = dx * sin_angle + dy * cos_angle; return new FloatPoint(p_pole.x + new_dx, p_pole.y + new_dy); } /** * Turns this FloatPoint by p_factor times 90 degree around ZERO. */ public FloatPoint turn_90_degree(int p_factor) { int n = p_factor; while (n < 0) { n += 4; } while (n >= 4) { n -= 4; } double new_x ; double new_y ; switch (n) { case 0: // 0 degree new_x = x; new_y = y; break; case 1: // 90 degree new_x = -y ; new_y = x ; break; case 2: // 180 degree new_x = -x ; new_y = -y ; break; case 3: // 270 degree new_x = y ; new_y = -x ; break; default: new_x = 0 ; new_y = 0 ; } return new FloatPoint(new_x, new_y); } /** * Turns this FloatPoint by p_factor times 90 degree around p_pole. */ public FloatPoint turn_90_degree(int p_factor, FloatPoint p_pole) { FloatPoint v = this.substract(p_pole); v = v.turn_90_degree(p_factor); return p_pole.add(v); } /** * Checks, if this point is contained in the box spanned by p_1 and p_2 with the input tolerance. */ public boolean is_contained_in_box(FloatPoint p_1, FloatPoint p_2, double p_tolerance) { double min_x; double max_x; if (p_1.x < p_2.x) { min_x = p_1.x; max_x = p_2.x; } else { min_x = p_2.x; max_x = p_1.x; } if (this.x < min_x - p_tolerance || this.x > max_x + p_tolerance) { return false; } double min_y; double max_y; if (p_1.y < p_2.y) { min_y = p_1.y; max_y = p_2.y; } else { min_y = p_2.y; max_y = p_1.y; } return (this.y >= min_y - p_tolerance && this.y <= max_y + p_tolerance); } /** * Creates the smallest IntBox containing this point. */ public IntBox bounding_box() { IntPoint lower_left = new IntPoint((int)Math.floor(this.x),(int)Math.floor(this.y)); IntPoint upper_right = new IntPoint((int)Math.ceil(this.x),(int)Math.ceil(this.y)); return new IntBox(lower_left, upper_right); } /** * Calculates the touching points of the tangents from this point to a circle * around p_to_point with radius p_distance. * Solves the quadratic equation, which results by substituting x by the * term in y from the equation of the polar line of a circle with center * p_to_point and radius p_distance and putting it into the circle * equation. The polar line is the line through the 2 tangential points * of the circle looked at from from this point and * has the equation * (this.x - p_to_point.x) * (x - p_to_point.x) * + (this.y - p_to_point.y) * (y - p_to_point.y) = p_distance **2 */ public FloatPoint[] tangential_points(FloatPoint p_to_point, double p_distance) { // turn the situation 90 degree if the x difference is smaller // than the y difference for better numerical stability double dx = Math.abs(this.x - p_to_point.x); double dy = Math.abs(this.y - p_to_point.y); boolean situation_turned = (dy > dx); FloatPoint pole; FloatPoint circle_center; if (situation_turned) { // turn the situation by 90 degree pole = new FloatPoint(-this.y, this.x); circle_center = new FloatPoint(-p_to_point.y, p_to_point.x); } else { pole = this; circle_center = p_to_point; } dx = pole.x - circle_center.x; dy = pole.y - circle_center.y; double dx_square = dx * dx; double dy_square = dy * dy; double dist_square = dx_square + dy_square; double radius_square = p_distance * p_distance; double discriminant = radius_square * dy_square - (radius_square - dx_square) * dist_square; if (discriminant <= 0) { // pole is inside the circle. return new FloatPoint[0]; } double square_root = Math.sqrt(discriminant); FloatPoint[] result = new FloatPoint[2]; double a1 = radius_square * dy; double dy1 = (a1 + p_distance * square_root) / dist_square; double dy2 = (a1 - p_distance * square_root) / dist_square; double first_point_y = dy1 + circle_center.y; double first_point_x = (radius_square - dy * dy1) / dx + circle_center.x; double second_point_y = dy2 + circle_center.y; double second_point_x = (radius_square - dy * dy2) / dx + circle_center.x; if (situation_turned) { // turn the result by 270 degree result[0] = new FloatPoint(first_point_y, -first_point_x); result[1] = new FloatPoint(second_point_y, -second_point_x); } else { result[0] = new FloatPoint(first_point_x, first_point_y); result[1] = new FloatPoint(second_point_x, second_point_y); } return result; } /** * Calculates the left tangential point of the line from this point * to a circle around p_to_point with radius p_distance. * Returns null, if this point is inside this circle. */ public FloatPoint left_tangential_point(FloatPoint p_to_point, double p_distance) { if (p_to_point == null) { return null; } FloatPoint[] tangent_points = tangential_points(p_to_point, p_distance); if (tangent_points.length < 2) { return null; } FloatPoint result; if (p_to_point.side_of(this, tangent_points[0]) == Side.ON_THE_RIGHT) { result = tangent_points[0]; } else { result = tangent_points[1]; } return result; } /** * Calculates the right tangential point of the line from this point * to a circle around p_to_point with radius p_distance. * Returns null, if this point is inside this circle. */ public FloatPoint right_tangential_point(FloatPoint p_to_point, double p_distance) { if (p_to_point == null) { return null; } FloatPoint[] tangent_points = tangential_points(p_to_point, p_distance); if (tangent_points.length < 2) { return null; } FloatPoint result; if (p_to_point.side_of(this, tangent_points[0]) == Side.ON_THE_LEFT) { result = tangent_points[0]; } else { result = tangent_points[1]; } return result; } /** * Calculates the center of the circle through this point, p_1 and p_2 * by calculating the intersection of the two lines perpendicular to and passing through * the midpoints of the lines (this, p_1) and (p_1, p_2). */ public FloatPoint circle_center(FloatPoint p_1, FloatPoint p_2) { double slope_1 = (p_1.y - this.y)/(p_1.x - this.x); double slope_2 = (p_2.y - p_1.y)/(p_2.x - p_1.x); double x_center = (slope_1 * slope_2 * (this.y -p_2.y) + slope_2 * (this.x + p_1.x) - slope_1 *(p_1.x + p_2.x)) /(2 * (slope_2 - slope_1)); double y_center = (0.5 * (this.x + p_1.x) - x_center)/slope_1 + 0.5 * (this.y + p_1.y); return new FloatPoint(x_center, y_center); } /** * Returns true, if this point is contained in the circle through p_1, p_2 and p_3. */ public boolean inside_circle(FloatPoint p_1, FloatPoint p_2, FloatPoint p_3) { FloatPoint center = p_1.circle_center(p_2, p_3); double radius_square = center.distance_square(p_1); return (this.distance_square(center) < radius_square - 1); // - 1 is a tolerance for numerical stability. } public String to_string(java.util.Locale p_locale) { java.text.NumberFormat nf = java.text.NumberFormat.getInstance(p_locale); nf.setMaximumFractionDigits(4); return (" (" + nf.format(x) + " , " + nf.format(y) + ") "); } public String toString() { return to_string(java.util.Locale.ENGLISH); } /** * Calculates the smallest IntOctagon containing all the input points */ public static IntOctagon bounding_octagon(FloatPoint [] p_point_arr) { 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 < p_point_arr.length; ++i) { FloatPoint curr = p_point_arr[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); } IntOctagon surrounding_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 surrounding_octagon; } /** * the x coordinate of this point */ public final double x; /** * the y coordinate of this point */ public final double y; }