/*
* Copyright 2010-2015 Institut Pasteur.
*
* This file is part of Icy.
*
* Icy 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.
*
* Icy 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with Icy. If not, see <http://www.gnu.org/licenses/>.
*/
package icy.type.point;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.List;
/**
* Point3D class.<br>
* Incomplete implementation (work in progress...)
*
* @author Stephane
*/
public abstract class Point3D implements Cloneable
{
/**
* Returns distance between 2 Point2D using specified scale factor for x/y/z dimension.
*/
public static double getDistance(Point3D pt1, Point3D pt2, double factorX, double factorY, double factorZ)
{
double px = (pt2.getX() - pt1.getX()) * factorX;
double py = (pt2.getY() - pt1.getY()) * factorY;
double pz = (pt2.getZ() - pt1.getZ()) * factorZ;
return Math.sqrt(px * px + py * py + pz * pz);
}
/**
* Returns total distance of the specified list of points.
*/
public static double getTotalDistance(List<Point3D> points, double factorX, double factorY, double factorZ,
boolean connectLastPoint)
{
final int size = points.size();
double result = 0d;
if (size > 1)
{
for (int i = 0; i < size - 1; i++)
result += getDistance(points.get(i), points.get(i + 1), factorX, factorY, factorZ);
// add last to first point distance
if (connectLastPoint)
result += getDistance(points.get(size - 1), points.get(0), factorX, factorY, factorZ);
}
return result;
}
/**
* Cache the hash code to make computing hashes faster.
*/
int hash = 0;
/**
* Returns the X coordinate of this <code>Point3D</code> in <code>double</code> precision.
*
* @return the X coordinate of this <code>Point3D</code>.
*/
public abstract double getX();
/**
* Returns the Y coordinate of this <code>Point3D</code> in <code>double</code> precision.
*
* @return the Y coordinate of this <code>Point3D</code>.
*/
public abstract double getY();
/**
* Returns the Z coordinate of this <code>Point3D</code> in <code>double</code> precision.
*
* @return the Z coordinate of this <code>Point3D</code>.
*/
public abstract double getZ();
/**
* Sets the X coordinate of this <code>Point3D</code> in <code>double</code> precision.
*/
public abstract void setX(double x);
/**
* Sets the Y coordinate of this <code>Point3D</code> in <code>double</code> precision.
*/
public abstract void setY(double y);
/**
* Sets the Z coordinate of this <code>Point3D</code> in <code>double</code> precision.
*/
public abstract void setZ(double z);
/**
* Sets the location of this <code>Point3D</code> to the
* specified <code>double</code> coordinates.
*
* @param x
* the new X coordinate of this {@code Point3D}
* @param y
* the new Y coordinate of this {@code Point3D}
* @param z
* the new Z coordinate of this {@code Point3D}
*/
public void setLocation(double x, double y, double z)
{
setX(x);
setY(y);
setZ(z);
}
/**
* Sets the location of this <code>Point3D</code> to the same
* coordinates as the specified <code>Point3D</code> object.
*
* @param p
* the specified <code>Point3D</code> to which to set
* this <code>Point3D</code>
*/
public void setLocation(Point3D p)
{
setLocation(p.getX(), p.getY(), p.getZ());
}
/**
* Translate this <code>Point3D</code> by the specified <code>double</code> coordinates.
*
* @param x
* the X translation value
* @param y
* the Y translation value
* @param z
* the Z translation value
*/
public void translate(double x, double y, double z)
{
setX(getX() + x);
setY(getY() + y);
setZ(getZ() + z);
}
/**
* Translate this <code>Point3D</code> by the specified <code>Point3D</code> coordinates.
*
* @param p
* the specified <code>Point3D</code> used to translate this <code>Point3D</code>
*/
public void translate(Point3D p)
{
translate(p.getX(), p.getY(), p.getZ());
}
/**
* Convert to 2D point
*/
public abstract Point2D toPoint2D();
/**
* Computes the distance between this point and point {@code (x1, y1, z1)}.
*
* @param x1
* the x coordinate of other point
* @param y1
* the y coordinate of other point
* @param z1
* the z coordinate of other point
* @return the distance between this point and point {@code (x1, y1, z1)}.
*/
public double distance(double x1, double y1, double z1)
{
double a = getX() - x1;
double b = getY() - y1;
double c = getZ() - z1;
return Math.sqrt(a * a + b * b + c * c);
}
/**
* Computes the distance between this point and the specified {@code point}.
*
* @param point
* the other point
* @return the distance between this point and the specified {@code point}.
* @throws NullPointerException
* if the specified {@code point} is null
*/
public double distance(Point3D point)
{
return distance(point.getX(), point.getY(), point.getZ());
}
/**
* Normalizes the relative magnitude vector represented by this instance.
* Returns a vector with the same direction and magnitude equal to 1.
* If this is a zero vector, a zero vector is returned.
*
* @return the normalized vector represented by a {@code Point3D} instance
*/
public Point3D normalize()
{
final double mag = magnitude();
if (mag == 0d)
return new Point3D.Double(0d, 0d, 0d);
return new Point3D.Double(getX() / mag, getY() / mag, getZ() / mag);
}
/**
* Returns a point which lies in the middle between this point and the
* specified coordinates.
*
* @param x
* the X coordinate of the second end point
* @param y
* the Y coordinate of the second end point
* @param z
* the Z coordinate of the second end point
* @return the point in the middle
*/
public Point3D midpoint(double x, double y, double z)
{
return new Point3D.Double(x + (getX() - x) / 2d, y + (getY() - y) / 2d, z + (getZ() - z) / 2d);
}
/**
* Returns a point which lies in the middle between this point and the
* specified point.
*
* @param point
* the other end point
* @return the point in the middle
* @throws NullPointerException
* if the specified {@code point} is null
*/
public Point3D midpoint(Point3D point)
{
return midpoint(point.getX(), point.getY(), point.getZ());
}
/**
* Computes the angle (in degrees) between the vector represented
* by this point and the specified vector.
*
* @param x
* the X magnitude of the other vector
* @param y
* the Y magnitude of the other vector
* @param z
* the Z magnitude of the other vector
* @return the angle between the two vectors measured in degrees
*/
public double angle(double x, double y, double z)
{
final double ax = getX();
final double ay = getY();
final double az = getZ();
final double delta = (ax * x + ay * y + az * z)
/ Math.sqrt((ax * ax + ay * ay + az * az) * (x * x + y * y + z * z));
if (delta > 1d)
return 0d;
if (delta < -1d)
return 180d;
return Math.toDegrees(Math.acos(delta));
}
/**
* Computes the angle (in degrees) between the vector represented
* by this point and the vector represented by the specified point.
*
* @param vector
* the other vector
* @return the angle between the two vectors measured in degrees, {@code NaN} if any of the two vectors is a zero
* vector
* @throws NullPointerException
* if the specified {@code vector} is null
*/
public double angle(Point3D vector)
{
return angle(vector.getX(), vector.getY(), vector.getZ());
}
/**
* Computes the angle (in degrees) between the three points with this point
* as a vertex.
*
* @param p1
* one point
* @param p2
* other point
* @return angle between the vectors (this, p1) and (this, p2) measured
* in degrees, {@code NaN} if the three points are not different
* from one another
* @throws NullPointerException
* if the {@code p1} or {@code p2} is null
*/
public double angle(Point3D p1, Point3D p2)
{
final double x = getX();
final double y = getY();
final double z = getZ();
final double ax = p1.getX() - x;
final double ay = p1.getY() - y;
final double az = p1.getZ() - z;
final double bx = p2.getX() - x;
final double by = p2.getY() - y;
final double bz = p2.getZ() - z;
final double delta = (ax * bx + ay * by + az * bz)
/ Math.sqrt((ax * ax + ay * ay + az * az) * (bx * bx + by * by + bz * bz));
if (delta > 1.0)
return 0.0;
if (delta < -1.0)
return 180.0;
return Math.toDegrees(Math.acos(delta));
}
/**
* Computes norm2 (square length) of the vector represented by this Point3D.
*
* @return norm2 length of the vector
*/
public double norm2()
{
final double x = getX();
final double y = getY();
final double z = getZ();
return x * x + y * y + z * z;
}
/**
* Computes length of the vector represented by this Point3D.
*
* @return length of the vector
*/
public double length()
{
return Math.sqrt(norm2());
}
/**
* Same as {@link #length()}
*/
public double magnitude()
{
return length();
}
/**
* Computes dot (scalar) product of the vector represented by this instance
* and the specified vector.
*
* @param x
* the X magnitude of the other vector
* @param y
* the Y magnitude of the other vector
* @param z
* the Z magnitude of the other vector
* @return the dot product of the two vectors
*/
public double dotProduct(double x, double y, double z)
{
return getX() * x + getY() * y + getZ() * z;
}
/**
* Computes dot (scalar) product of the vector represented by this instance
* and the specified vector.
*
* @param vector
* the other vector
* @return the dot product of the two vectors
* @throws NullPointerException
* if the specified {@code vector} is null
*/
public double dotProduct(Point3D vector)
{
return dotProduct(vector.getX(), vector.getY(), vector.getZ());
}
/**
* Computes cross product of the vector represented by this instance
* and the specified vector.
*
* @param x
* the X magnitude of the other vector
* @param y
* the Y magnitude of the other vector
* @param z
* the Z magnitude of the other vector
* @return the cross product of the two vectors
*/
public Point3D crossProduct(double x, double y, double z)
{
final double ax = getX();
final double ay = getY();
final double az = getZ();
return new Point3D.Double(ay * z - az * y, az * x - ax * z, ax * y - ay * x);
}
/**
* Computes cross product of the vector represented by this instance
* and the specified vector.
*
* @param vector
* the other vector
* @return the cross product of the two vectors
* @throws NullPointerException
* if the specified {@code vector} is null
*/
public Point3D crossProduct(Point3D vector)
{
return crossProduct(vector.getX(), vector.getY(), vector.getZ());
}
@Override
public boolean equals(Object obj)
{
if (obj == this)
return true;
if (obj instanceof Point3D)
{
final Point3D pt = (Point3D) obj;
return (getX() == pt.getX()) && (getY() == pt.getY()) && (getZ() == pt.getZ());
}
return super.equals(obj);
}
/**
* Creates a new object of the same class as this object.
*
* @return a clone of this instance.
* @exception OutOfMemoryError
* if there is not enough memory.
* @see java.lang.Cloneable
*/
@Override
public Object clone()
{
try
{
return super.clone();
}
catch (CloneNotSupportedException e)
{
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
/**
* @return a hash code for this {@code Point3D} object.
*/
@Override
public int hashCode()
{
if (hash == 0)
{
long bits = 7L;
bits = 31L * bits + java.lang.Double.doubleToLongBits(getX());
bits = 31L * bits + java.lang.Double.doubleToLongBits(getY());
bits = 31L * bits + java.lang.Double.doubleToLongBits(getZ());
hash = (int) (bits ^ (bits >> 32));
// so hash could never be 0 when computed
hash |= 1;
}
return hash;
}
/**
* Returns a string representation of this {@code Point3D}.
* This method is intended to be used only for informational purposes.
*/
@Override
public String toString()
{
return getClass().getName() + "[" + getX() + "," + getY() + "," + getZ() + "]";
}
public static class Double extends Point3D
{
/**
* Create an array of Point3D.Double from the input double array.<br>
* <br>
* The format of the input array should be as follow:<br>
* <code>input.lenght</code> = number of point * 3.<br>
* <code>input[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
* <code>input[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
* <code>input[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
*/
public static Point3D.Double[] toPoint3D(double[] input)
{
final Point3D.Double[] result = new Point3D.Double[input.length / 3];
int pt = 0;
for (int i = 0; i < input.length; i += 3)
result[pt++] = new Point3D.Double(input[i + 0], input[i + 1], input[i + 2]);
return result;
}
/**
* Create an array of double from the input Point3D.Double array.<br>
* <br>
* The format of the output array is as follow:<br>
* <code>result.lenght</code> = number of point * 3.<br>
* <code>result[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
* <code>result[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
* <code>result[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
*/
public static double[] toDoubleArray(Point3D.Double[] input)
{
final double[] result = new double[input.length * 3];
int off = 0;
for (Point3D.Double pt : input)
{
result[off++] = pt.x;
result[off++] = pt.y;
result[off++] = pt.z;
}
return result;
}
public double x;
public double y;
public double z;
public Double(double x, double y, double z)
{
super();
this.x = x;
this.y = y;
this.z = z;
}
public Double(double[] xyz)
{
final int len = xyz.length;
if (len > 0)
this.x = xyz[0];
if (len > 1)
this.y = xyz[1];
if (len > 2)
this.z = xyz[2];
}
public Double()
{
this(0, 0, 0);
}
@Override
public double getX()
{
return x;
}
@Override
public void setX(double x)
{
this.x = x;
hash = 0;
}
@Override
public double getY()
{
return y;
}
@Override
public void setY(double y)
{
this.y = y;
hash = 0;
}
@Override
public double getZ()
{
return z;
}
@Override
public void setZ(double z)
{
this.z = z;
hash = 0;
}
@Override
public String toString()
{
return "Point3D.Double[" + x + "," + y + "," + z + "]";
}
@Override
public Point2D toPoint2D()
{
return new Point2D.Double(x, y);
}
}
public static class Float extends Point3D
{
/**
* Create an array of Point3D.Float from the input float array.<br>
* <br>
* The format of the input array should be as follow:<br>
* <code>input.lenght</code> = number of point * 3.<br>
* <code>input[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
* <code>input[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
* <code>input[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
*/
public static Point3D.Float[] toPoint3D(float[] input)
{
final Point3D.Float[] result = new Point3D.Float[input.length / 3];
int pt = 0;
for (int i = 0; i < input.length; i += 3)
result[pt++] = new Point3D.Float(input[i + 0], input[i + 1], input[i + 2]);
return result;
}
/**
* Create an array of float from the input Point3D.Float array.<br>
* <br>
* The format of the output array is as follow:<br>
* <code>result.lenght</code> = number of point * 3.<br>
* <code>result[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
* <code>result[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
* <code>result[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
*/
public static float[] toFloatArray(Point3D.Float[] input)
{
final float[] result = new float[input.length * 3];
int off = 0;
for (Point3D.Float pt : input)
{
result[off++] = pt.x;
result[off++] = pt.y;
result[off++] = pt.z;
}
return result;
}
public float x;
public float y;
public float z;
public Float(float x, float y, float z)
{
super();
this.x = x;
this.y = y;
this.z = z;
}
public Float(float[] xyz)
{
final int len = xyz.length;
if (len > 0)
this.x = xyz[0];
if (len > 1)
this.y = xyz[1];
if (len > 2)
this.z = xyz[2];
}
public Float()
{
this(0, 0, 0);
}
@Override
public double getX()
{
return x;
}
@Override
public void setX(double x)
{
this.x = (float) x;
hash = 0;
}
@Override
public double getY()
{
return y;
}
@Override
public void setY(double y)
{
this.y = (float) y;
hash = 0;
}
@Override
public double getZ()
{
return z;
}
@Override
public void setZ(double z)
{
this.z = (float) z;
hash = 0;
}
@Override
public String toString()
{
return "Point3D.Float[" + x + "," + y + "," + z + "]";
}
@Override
public Point2D toPoint2D()
{
return new Point2D.Float(x, y);
}
}
public static class Integer extends Point3D
{
/**
* Create an array of Point3D.Integer from the input integer array.<br>
* <br>
* The format of the input array should be as follow:<br>
* <code>input.lenght</code> = number of point * 3.<br>
* <code>input[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
* <code>input[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
* <code>input[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
*/
public static Point3D.Integer[] toPoint3D(int[] input)
{
final Point3D.Integer[] result = new Point3D.Integer[input.length / 3];
int pt = 0;
for (int i = 0; i < input.length; i += 3)
result[pt++] = new Point3D.Integer(input[i + 0], input[i + 1], input[i + 2]);
return result;
}
/**
* Create an array of integer from the input Point3D.Integer array.<br>
* <br>
* The format of the output array is as follow:<br>
* <code>result.lenght</code> = number of point * 3.<br>
* <code>result[(pt * 3) + 0]</code> = X coordinate for point <i>pt</i><br>
* <code>result[(pt * 3) + 1]</code> = Y coordinate for point <i>pt</i><br>
* <code>result[(pt * 3) + 2]</code> = Z coordinate for point <i>pt</i><br>
*/
public static int[] toIntegerArray(Point3D.Integer[] input)
{
final int[] result = new int[input.length * 3];
int off = 0;
for (Point3D.Integer pt : input)
{
result[off++] = pt.x;
result[off++] = pt.y;
result[off++] = pt.z;
}
return result;
}
public int x;
public int y;
public int z;
public Integer(int x, int y, int z)
{
super();
this.x = x;
this.y = y;
this.z = z;
}
public Integer(int[] xyz)
{
final int len = xyz.length;
if (len > 0)
this.x = xyz[0];
if (len > 1)
this.y = xyz[1];
if (len > 2)
this.z = xyz[2];
}
public Integer()
{
this(0, 0, 0);
}
@Override
public double getX()
{
return x;
}
@Override
public void setX(double x)
{
this.x = (int) x;
hash = 0;
}
@Override
public double getY()
{
return y;
}
@Override
public void setY(double y)
{
this.y = (int) y;
hash = 0;
}
@Override
public double getZ()
{
return z;
}
@Override
public void setZ(double z)
{
this.z = (int) z;
hash = 0;
}
@Override
public String toString()
{
return "Point3D.Integer[" + x + "," + y + "," + z + "]";
}
@Override
public Point2D toPoint2D()
{
return new Point(x, y);
}
}
}