/*
* 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.
*
* FloatLine.java
*
* Created on 19. Februar 2004, 07:22
*/
package geometry.planar;
/**
* Defines a line in the plane by to FloatPoints.
* Calculations with FloatLines are generally not exact.
* For that reason collinearity for example is not defined for FloatLines.
* If exactnesss is needed, use the class Line instead.
*
* @author Alfons Wirtz
*/
public class FloatLine
{
/**
* Creates a line from two FloatPoints.
*/
public FloatLine(FloatPoint p_a, FloatPoint p_b)
{
if (p_a == null || p_b == null)
{
System.out.println("FloatLine: Parameter is null");
}
a = p_a;
b = p_b;
}
/**
* Returns the FloatLine with swapped end points.
*/
public FloatLine opposite()
{
return new FloatLine(this.b, this.a);
}
public FloatLine adjust_direction(FloatLine p_other)
{
if (this.b.side_of(this.a, p_other.a )== p_other.b.side_of(this.a, p_other.a ))
{
return this;
}
return this.opposite();
}
/**
* Calculates the intersection of this line with p_other.
* Returns null, if the lines are parallel.
*/
public FloatPoint intersection(FloatLine p_other)
{
double d1x = this.b.x - this.a.x;
double d1y = this.b.y - this.a.y;
double d2x = p_other.b.x - p_other.a.x;
double d2y = p_other.b.y - p_other.a.y;
double det_1 = this.a.x * this.b.y - this.a.y * this.b.x;
double det_2 = p_other.a.x * p_other.b.y - p_other.a.y * p_other.b.x;
double det = d2x * d1y - d2y * d1x;
double is_x;
double is_y;
if(det == 0)
{
return null;
}
is_x = (d2x * det_1 - d1x * det_2) / det;
is_y = (d2y * det_1 - d1y * det_2) / det;
return new FloatPoint(is_x, is_y);
}
/**
* translates the line perpendicular at about p_dist.
* If p_dist > 0, the line will be translated to the left, else to the right
*/
public FloatLine translate(double p_dist)
{
double dx = b.x - a.x;
double dy = b.y - a.y;
double dxdx = dx * dx;
double dydy = dy * dy;
double lenght = Math.sqrt(dxdx + dydy);
FloatPoint new_a;
if (dxdx <= dydy)
{
// translate along the x axis
double rel_x = (p_dist * lenght) / dy;
new_a = new FloatPoint(this.a.x - rel_x, this.a.y);
}
else
{
// translate along the y axis
double rel_y = (p_dist * lenght) / dx;
new_a = new FloatPoint(this.a.x, this.a.y + rel_y);
}
FloatPoint new_b = new FloatPoint(new_a.x + dx, new_a.y + dy);
return new FloatLine(new_a, new_b);
}
/**
* Returns the signed distance of this line from p_point.
* The result will be positive, if the line is on the left of p_point,
* else negative.
*/
public double signed_distance(FloatPoint p_point)
{
double dx = this.b.x - this.a.x;
double dy = this.b.y - this.a.y;
double det =
dy * (p_point.x - this.a.x) -
dx * (p_point.y - this.a.y);
// area of the parallelogramm spanned by the 3 points
double length = Math.sqrt(dx * dx + dy * dy);
return det / length;
}
/**
* Returns an approximation of the perpensicular projection
* of p_point onto this line.
*/
public FloatPoint perpendicular_projection(FloatPoint p_point)
{
double dx = b.x - a.x;
double dy = b.y - a.y;
if (dx == 0 && dy == 0)
{
return this.a;
}
double dxdx = dx * dx;
double dydy = dy * dy;
double dxdy = dx * dy;
double denominator = dxdx + dydy;
double det = a.x * b.y - b.x * a.y;
double x = (p_point.x * dxdx + p_point.y * dxdy + det * dy) / denominator;
double y = (p_point.x * dxdy + p_point.y * dydy - det * dx) / denominator;
return new FloatPoint(x, y);
}
/**
* Returns the distance of p_point to the nearest point of this line
* betweem this.a and this.b.
*/
public double segment_distance(FloatPoint p_point)
{
FloatPoint projection = perpendicular_projection(p_point);
double result;
if (projection.is_contained_in_box(this.a, this.b, 0.01))
{
result = p_point.distance(projection);
}
else
{
result = Math.min(p_point.distance(a), p_point.distance(b));
}
return result;
}
/**
* Returns the perpendicular projection of p_line_segment onto this oriented line segment,
* Returns null, if the projection is empty.
*/
public FloatLine segment_projection(FloatLine p_line_segment)
{
if (this.b.scalar_product(this.a, p_line_segment.a) < 0)
{
return null;
}
if (this.a.scalar_product(this.b, p_line_segment.b) < 0)
{
return null;
}
FloatPoint projected_a;
if (this.a.scalar_product(this.b, p_line_segment.a) < 0)
{
projected_a = this.a;
}
else
{
projected_a = this.perpendicular_projection(p_line_segment.a);
if (Math.abs(projected_a.x) >= Limits.CRIT_INT || Math.abs(projected_a.y) >= Limits.CRIT_INT)
{
return null;
}
}
FloatPoint projected_b;
if (this.b.scalar_product(this.a, p_line_segment.b) < 0)
{
projected_b = this.b;
}
else
{
projected_b = this.perpendicular_projection(p_line_segment.b);
}
if (Math.abs(projected_b.x) >= Limits.CRIT_INT || Math.abs(projected_b.y) >= Limits.CRIT_INT)
{
return null;
}
return new FloatLine(projected_a, projected_b);
}
/**
* Returns the projection of p_line_segment onto this oriented line segment
* by moving p_line_segment perpendicular into the direction of this line segmant
* Returns null, if the projection is empty or p_line_segment.a == p_line_segment.b
*/
public FloatLine segment_projection_2(FloatLine p_line_segment)
{
if (p_line_segment.a.scalar_product(p_line_segment.b, this.b) <= 0)
{
return null;
}
if ( p_line_segment.b.scalar_product(p_line_segment.a, this.a) <= 0)
{
return null;
}
FloatPoint projected_a;
if (p_line_segment.a.scalar_product(p_line_segment.b, this.a) < 0)
{
FloatLine curr_perpendicular_line =
new FloatLine(p_line_segment.a, p_line_segment.b.turn_90_degree(1, p_line_segment.a));
projected_a = curr_perpendicular_line.intersection(this);
if (projected_a == null || Math.abs(projected_a.x) >= Limits.CRIT_INT || Math.abs(projected_a.y) >= Limits.CRIT_INT)
{
return null;
}
}
else
{
projected_a = this.a;
}
FloatPoint projected_b;
if (p_line_segment.b.scalar_product(p_line_segment.a, this.b) < 0)
{
FloatLine curr_perpendicular_line =
new FloatLine(p_line_segment.b, p_line_segment.a.turn_90_degree(1, p_line_segment.b));
projected_b = curr_perpendicular_line.intersection(this);
if (projected_b == null || Math.abs(projected_b.x) >= Limits.CRIT_INT || Math.abs(projected_b.y) >= Limits.CRIT_INT)
{
return null;
}
}
else
{
projected_b = this.b;
}
return new FloatLine(projected_a, projected_b);
}
/**
* Shrinks this line on both sides by p_value.
* The result will contain at least the gravity point of the line.
*/
public FloatLine shrink_segment(double p_offset)
{
double dx = b.x - a.x;
double dy = b.y - a.y;
if (dx == 0 && dy == 0)
{
return this;
}
double length = Math.sqrt(dx * dx + dy * dy);
double offset = Math.min(p_offset, length/2);
FloatPoint new_a = new FloatPoint(a.x + (dx * offset) / length, a.y + (dy * offset) / length);
double new_length = length - offset;
FloatPoint new_b = new FloatPoint(a.x + (dx * new_length) / length, a.y + (dy * new_length) / length);
return new FloatLine(new_a, new_b);
}
/**
* Calculates the nearest point on this line to p_from_point between
* this.a and this.b.
*/
public FloatPoint nearest_segment_point(FloatPoint p_from_point)
{
FloatPoint projection = this.perpendicular_projection(p_from_point);
if (projection.is_contained_in_box(this.a, this.b, 0.01))
{
return projection;
}
// Now the projection is outside the line segment.
FloatPoint result;
if (p_from_point.distance_square(this.a) <= p_from_point.distance_square(this.b))
{
result = this.a;
}
else
{
result = this.b;
}
return result;
}
/**
* Divides this line segment into p_count line segments of nearly equal length.
* and at most p_max_section_length.
*/
public FloatLine[] divide_segment_into_sections(int p_count)
{
if (p_count == 0)
{
return new FloatLine[0];
}
if (p_count == 1)
{
FloatLine [] result = new FloatLine[1];
result[0] = this;
return result;
}
double line_length = this.b.distance(this.a);
FloatLine[] result = new FloatLine[p_count];
double section_length = line_length / p_count;
double dx = b.x - a.x;
double dy = b.y - a.y;
FloatPoint curr_a = this.a;
for (int i = 0; i < p_count; ++i)
{
FloatPoint curr_b;
if (i == p_count - 1)
{
curr_b = this.b;
}
else
{
double curr_b_dist = (i + 1) * section_length;
double curr_b_x = a.x + (dx * curr_b_dist)/line_length;
double curr_b_y = a.y + (dy * curr_b_dist)/line_length;
curr_b = new FloatPoint(curr_b_x, curr_b_y);
}
result[i] = new FloatLine(curr_a, curr_b);
curr_a = curr_b;
}
return result;
}
public final FloatPoint a;
public final FloatPoint b;
}