/*
* Copyright (c) 2016 Fraunhofer IGD
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Fraunhofer IGD <http://www.igd.fraunhofer.de/>
*/
package de.fhg.igd.geom;
import java.io.Serializable;
import java.util.List;
import de.fhg.igd.geom.util.BlochHashCode;
/**
* This is a base class for the description of a Point in 3D space (using double
* coordinates).
*
* @author Thorsten Reitz
*/
public class Point3D implements Localizable, Serializable, Cloneable {
/**
* The class' serial version UID
*/
private static final long serialVersionUID = 7639127877097165234L;
/**
* This Point's x coordinate component.
*/
private double x;
/**
* This Point's y coordinate component.
*/
private double y;
/**
* This Point's z coordinate component.
*/
private double z;
/**
* Default constructor
*/
public Point3D() {
super();
}
/**
* Constructs a new Point with the x and y coordinate of a 2D point
*
* @param p2d the 2D point
*/
public Point3D(Point2D p2d) {
this.x = p2d.getX();
this.y = p2d.getY();
this.z = 0;
}
/**
* Constructs a new Point with the x and y coordinate of a 2D point.
*
* @param p2d the 2D point
* @param z the missing z coordinate
*/
public Point3D(Point2D p2d, double z) {
this.x = p2d.getX();
this.y = p2d.getY();
this.z = z;
}
/**
* Constructs a new Point with the x, y and z coordinates of another 3D
* point
*
* @param p3d the other 3D point
*/
public Point3D(Point3D p3d) {
this.x = p3d.getX();
this.y = p3d.getY();
this.z = p3d.getZ();
}
/**
* Constructs a Point with the given ordinates
*
* @param x the x ordinate
* @param y the y ordinate
* @param z the z ordinate
*/
public Point3D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
// functional methods ......................................................
/**
* @return A BoundingBox for this Point, with min == max values.
*/
@Override
public BoundingBox getBoundingBox() {
return new BoundingBox(this.x, this.y, this.z, this.x, this.y, this.z);
}
// canonical java methods ..................................................
/**
* @see Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (!(o instanceof Point3D)) {
return false;
}
Point3D other = (Point3D) o;
if (this.x == other.getX() && this.y == other.getY() && this.z == other.getZ()) {
return true;
}
return false;
}
/**
* Provides a hashCode so that x.hashCode() == y.hashCode() when x.equals(y)
* == true
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
int hash = BlochHashCode.addFieldToHash(BlochHashCode.HASH_CONSTANT, this.x);
hash = BlochHashCode.addFieldToHash(hash, this.y);
return BlochHashCode.addFieldToHash(hash, this.z);
}
/**
* @see Object#toString()
*/
@Override
public String toString() {
String result = "[Point3D: ";
return (result + this.getX() + ", " + this.getY() + ", " + this.getZ() + "]");
}
// getter/setter methods ...................................................
/**
* @return the x ordinate
*/
public double getX() {
return this.x;
}
/**
* @return the y ordinate
*/
public double getY() {
return this.y;
}
/**
* @return the z ordinate
*/
public double getZ() {
return this.z;
}
/**
* @param p3d the other 3D point
*/
public void set(Point3D p3d) {
this.x = p3d.getX();
this.y = p3d.getY();
this.z = p3d.getZ();
}
/**
* @param x the new x ordinate
* @param y the new y ordinate
* @param z the new z ordinate
*/
public void set(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
/**
* Sets the new x ordinate
*
* @param x the new ordinate
*/
public void setX(double x) {
this.x = x;
}
/**
* Sets the new y ordinate
*
* @param y the new ordinate
*/
public void setY(double y) {
this.y = y;
}
/**
* Sets the new z ordinate
*
* @param z the new ordinate
*/
public void setZ(double z) {
this.z = z;
}
/**
* @see Object#clone()
*/
@Override
public Point3D clone() throws CloneNotSupportedException {
return (Point3D) super.clone();
}
/**
* Calculates the normal of a given vertex p2.
*
* @param p1 the point prior to p2
* @param p2 the point to calculate the normal for
* @param p3 the point next to p2
* @return the normal of p2
*/
public static Point3D calcNormal(Point3D p1, Point3D p2, Point3D p3) {
return calcNormal(p1.getX(), p1.getY(), p1.getZ(), p2.getX(), p2.getY(), p2.getZ(),
p3.getX(), p3.getY(), p3.getZ());
}
/**
* Calculates the normal of a given vertex p2.
*
* @param p1x the x ordinate of the point prior to p2
* @param p1y the y ordinate of the point prior to p2
* @param p1z the z ordinate of the point prior to p2
* @param p2x the x ordinate of the point to calculate the normal for
* @param p2y the y ordinate of the point to calculate the normal for
* @param p2z the z ordinate of the point to calculate the normal for
* @param p3x the x ordinate of the point next to p2
* @param p3y the y ordinate of the point next to p2
* @param p3z the z ordinate of the point next to p2
* @return the normal of p2
*/
public static Point3D calcNormal(double p1x, double p1y, double p1z, double p2x, double p2y,
double p2z, double p3x, double p3y, double p3z) {
double ax = p3x - p2x;
double ay = p3y - p2y;
double az = p3z - p2z;
double bx = p1x - p2x;
double by = p1y - p2y;
double bz = p1z - p2z;
// calculate vector product
double x = ay * bz - az * by;
double y = az * bx - ax * bz;
double z = ax * by - ay * bx;
// normalize vector
double length = Math.sqrt(x * x + y * y + z * z);
if (length == 0) {
// a and b are parallel
x = 0.0;
y = 0.0;
z = 1.0;
}
else {
x /= length;
y /= length;
z /= length;
}
return new Point3D(x, y, z);
}
/**
* Calculates the average normal of a face by calculating the normals of all
* vertices. There must be at least 3 vertices in this face, otherwise this
* method returns null.
*
* @param vs the list of points representing a face
*
* @return the normal of the face
*/
public static Point3D calcNormal(List<Point3D> vs) {
int vslen = vs.size();
if (vslen < 3) {
return null;
}
// also calculate normal correctly if polygon is closed. refs #811
if (vs.get(0).equals(vs.get(vslen - 1))) {
--vslen;
if (vslen < 3) {
return null;
}
}
// calculate normal for the first vertex
Point3D normal = calcNormal(vs.get(vslen - 1), vs.get(0), vs.get(1));
if (vslen == 3) {
// already done
return normal;
}
double x = normal.getX();
double y = normal.getY();
double z = normal.getZ();
// calculate normal for each vertex and add it to the normal
// calculated above to create an average normal
for (int i = 1; i < vslen; ++i) {
// calculate current vertex' normal
Point3D vn;
if (i < vslen - 1) {
vn = calcNormal(vs.get(i - 1), vs.get(i), vs.get(i + 1));
}
else {
vn = calcNormal(vs.get(i - 1), vs.get(i), vs.get(0));
}
// if current normal has another direction than the average
// normal, turn it around
double innerProduct = x * vn.getX() + y * vn.getY() + z * vn.getZ();
if (innerProduct < 0.0) {
// invert current normal
vn.setX(-vn.getX());
vn.setY(-vn.getY());
vn.setZ(-vn.getZ());
}
// add both normals
x += vn.getX();
y += vn.getY();
z += vn.getZ();
}
// normalize normal
double length = Math.sqrt(x * x + y * y + z * z);
if (length == 0) {
// a and b are parallel
x = 0.0;
y = 0.0;
z = 1.0;
}
else {
x /= length;
y /= length;
z /= length;
}
normal.setX(x);
normal.setY(y);
normal.setZ(z);
return normal;
}
}