/*
* 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.
*
* Direction.java
*
* Created on 3. Februar 2003, 15:36
*/
package geometry.planar;
import datastructures.Signum;
/**
*
* Abstract class defining functionality of directions
* in the plane. A Direction is an equivalence class of
* vectors. Two vectors define the same object of class
* Direction, if they point into the same direction.
* We prefer using directions instead of angles, because
* with angles the arithmetic calculations are in
* general not exact.
*
* @author Alfons Wirtz
*/
public abstract class Direction implements Comparable<Direction>, java.io.Serializable
{
public static final IntDirection NULL = new IntDirection(0,0);
/**
* the direction to the east
*/
public static final IntDirection RIGHT = new IntDirection(1, 0);
/**
* the direction to the northeast
*/
public static final IntDirection RIGHT45 = new IntDirection(1, 1);
/**
* the direction to the north
*/
public static final IntDirection UP = new IntDirection(0, 1);
/**
* the direction to the northwest
*/
public static final IntDirection UP45 = new IntDirection(-1, 1);
/**
* the direction to the west
*/
public static final IntDirection LEFT = new IntDirection(-1, 0);
/**
* the direction to the southwest
*/
public static final IntDirection LEFT45 = new IntDirection(-1, -1);
/**
* the direction to the south
*/
public static final IntDirection DOWN = new IntDirection(0, -1);
/**
* the direction to the southeast
*/
public static final IntDirection DOWN45 = new IntDirection(1, -1);
/**
* creates a Direction from the input Vector
*/
public static Direction get_instance( Vector p_vector )
{
return p_vector.to_normalized_direction();
}
/**
* Calculates the direction from p_from to p_to.
* If p_from and p_to are equal, null is returned.
*/
public static Direction get_instance( Point p_from, Point p_to )
{
if ( p_from.equals(p_to) )
{
return null;
}
return get_instance(p_to.difference_by( p_from ));
}
/**
* Creates a Direction whose angle with the x-axis is nearly equal to p_angle
*/
public static Direction get_instance_approx( double p_angle )
{
final double scale_factor = 10000;
int x = (int)Math.round(Math.cos(p_angle) * scale_factor);
int y = (int)Math.round(Math.sin(p_angle) * scale_factor);
return get_instance(new IntVector(x, y));
}
/**
* return any Vector pointing into this direction
*/
public abstract Vector get_vector();
/**
* returns true, if the direction is horizontal or vertical
*/
public abstract boolean is_orthogonal();
/**
* returns true, if the direction is diagonal
*/
public abstract boolean is_diagonal();
/**
* returns true, if the direction is orthogonal or diagonal
*/
public boolean is_multiple_of_45_degree()
{
return ( is_orthogonal() || is_diagonal() ) ;
}
/**
* turns the direction by p_factor times 45 degree
*/
public abstract Direction turn_45_degree(int p_factor);
/**
* returns the opposite direction of this direction
*/
public abstract Direction opposite();
/**
* Returns true, if p_ob is a Direction and
* this Direction and p_ob point into the same direction
*/
public final boolean equals( Direction p_other )
{
if ( this == p_other )
{
return true;
}
if ( p_other == null )
{
return false;
}
if (this.side_of(p_other) != Side.COLLINEAR)
{
return false;
}
// check, that dir and other_dir do not point into opposite directions
Vector this_vector = get_vector();
Vector other_vector = p_other.get_vector() ;
return this_vector.projection(other_vector) == Signum.POSITIVE;
}
/**
* Let L be the line from the Zero Vector to p_other.get_vector().
* The function returns
* Side.ON_THE_LEFT, if this.get_vector() is on the left of L
* Side.ON_THE_RIGHT, if this.get_vector() is on the right of L
* and Side.COLLINEAR, if this.get_vector() is collinear with L.
*/
public Side side_of(Direction p_other)
{
return this.get_vector().side_of(p_other.get_vector());
}
/**
* The function returns
* Signum.POSITIVE, if the scalar product of of a vector representing
* this direction and a vector representing p_other is > 0,
* Signum.NEGATIVE, if the scalar product is < 0,
* and Signum.ZERO, if the scalar product is equal 0.
*/
public Signum projection(Direction p_other)
{
return this.get_vector().projection(p_other.get_vector());
}
/**
* calculates an approximation of the direction in the middle of
* this direction and p_other
*/
public Direction middle_approx(Direction p_other)
{
FloatPoint v1 = get_vector().to_float();
FloatPoint v2 = p_other.get_vector().to_float();
double length1 = v1.size();
double length2 = v2.size();
double x = v1.x / length1 + v2.x /length2;
double y = v1.y / length1 + v2.y /length2;
final double scale_factor = 1000;
Vector vm = new IntVector((int)Math.round(x * scale_factor),
(int)Math.round(y * scale_factor));
return Direction.get_instance(vm);
}
/**
* Returns 1, if the angle between p_1 and this direction is bigger
* the angle between p_2 and this direction,
* 0, if p_1 is equal to p_2, * and -1 otherwise.
*/
public int compare_from(Direction p_1, Direction p_2)
{
int result;
if (p_1.compareTo(this) >= 0)
{
if (p_2.compareTo(this) >= 0)
{
result = p_1.compareTo(p_2);
}
else
{
result = -1;
}
}
else
{
if (p_2.compareTo(this) >= 0)
{
result = 1;
}
else
{
result = p_1.compareTo(p_2);
}
}
return result;
}
/**
* Returns an approximation of the signed angle corresponding to this dierection.
*/
public double angle_approx()
{
return this.get_vector().angle_approx();
}
// auxiliary functions needed because the virtual function mechanism
// does not work in parameter position
abstract int compareTo(IntDirection p_other);
abstract int compareTo(BigIntDirection p_other);
}