/*
 * 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 2 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 PURPOUSE. See the GNU
 * General Public License for more details.
 *
 * You should have recieved a copy of the GNU General Public License
 * along with this program; if not write to the Free Software
 * Foundation, inc., 59 Temple Place, Suite 330, Boston MA 02111-1307
 * USA
 */
/* 
 *
 * Point4.java
 *
 * author   Timothy F. Rohaly
 * version  1.0, 01/15/97
 */

package j3d.nurbs;


/**
 * Representation of a homogeneous point in 3-space, used
 * as control points for a NURBS curve or surface.
 * @author   Timothy F. Rohaly
 * @version  1.0, 1/15/97
 */
public class Point4 {

    public static final float TOLERANCE = 1.0E-7f;

    /**
     * The x coordinate.
     */
    public float x;

    /**
     * The y coordinate.
     */
    public float y;

    /**
     * The z coordinate.
     */
    public float z;

    /**
     * The w coordinate.
     */
    public float w;


    /**
     * Constructor.  Initializes coordinates to 0.0f.
     */
    public Point4() {
        this(0.0f, 0.0f, 0.0f, 0.0f);
    }


    /**
     * Constructor.
     * @param x the x coordinate.
     * @param y the y coordinate.
     * @param z the z coordinate.
     * @param w the w coordinate.
     */
    public Point4(float x, float y, float z, float w) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;
    }


    /**
     * Constructor.
     * @param point a Point4 object to copy.
     */
    public Point4(Point4 point) {
        this.x = point.x;
        this.y = point.y;
        this.z = point.z;
        this.w = point.w;
    }


    /**
     * Set the values of this Point4.
     * @param x the x coordinate.
     * @param y the y coordinate.
     * @param z the z coordinate.
     * @param w the w coordinate.
     */
    public void setValue(float x, float y, float z, float w) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;
    }


    /**
     * Check to see if this Point4 approximately equals
     * another.  "Approximately" means within one part
     * in ten million.  This is useful when comparing
     * to within roundoff error
     * @param p1 the Point4 to compare
     */
    public boolean approxEquals(Point4 p1) {
        if (Math.abs(this.x - p1.x) <= TOLERANCE &&
            Math.abs(this.y - p1.y) <= TOLERANCE &&
            Math.abs(this.z - p1.z) <= TOLERANCE &&
            Math.abs(this.w - p1.w) <= TOLERANCE    )
            return true;
        return false;
    }


    /**
     * Add another Point4 to this one.
     * @param p1 the Point4 to add
     */
    public void add(Point4 p1) {
        this.x += p1.x;
        this.y += p1.y;
        this.z += p1.z;
        this.w += p1.w;
    }


    /**
     * Translate this Point4.
     * @param x the X translation component
     * @param y the Y translation component
     * @param z the Z translation component
     */
    public void translate(float x, float y, float z) {
        this.x = this.x + this.w*x;
        this.y = this.y + this.w*y;
        this.z = this.z + this.w*z;
    }


    /**
     * Scale this Point4 uniformly.
     * @param scale the uniform scale factor
     */
    public void scale(float scale) {
        this.scale(scale, scale, scale);
    }


    /**
     * Scale this Point4 non-uniformly.
     * @param xscale the x scale factor
     * @param yscale the y scale factor
     * @param zscale the z scale factor
     */
    public void scale(float xscale, float yscale, float zscale) {
        this.x *= xscale;
        this.y *= yscale;
        this.z *= zscale;
    }


    /**
     * Apply a rotation transformation to this Point4.
     * @param xaxis the X component of the rotation axis
     * @param yaxis the Y component of the rotation axis
     * @param zaxis the Z component of the rotation axis
     * @param angle the angle of the rotation in radians
     */
    public void rotate(float xaxis, float yaxis, float zaxis, float angle) {
        float cos = (float) Math.cos(angle);
        float sin = (float) Math.sin(angle);
        float x = this.x * ( xaxis*xaxis  +  cos*(1f - xaxis*xaxis)) +
                  this.y * ( zaxis*sin    +  xaxis*yaxis*(1f - cos)) +
                  this.z * ( yaxis*sin    +  xaxis*zaxis*(1f - cos));
        float y = this.x * ( zaxis*sin    +  xaxis*yaxis*(1f - cos)) +
                  this.y * ( yaxis*yaxis  +  cos*(1f - yaxis*yaxis)) +
                  this.z * (-xaxis*sin    +  yaxis*zaxis*(1f - cos));
        float z = this.x * (-yaxis*sin    +  xaxis*zaxis*(1f - cos)) +
                  this.y * ( xaxis*sin    +  yaxis*zaxis*(1f - cos)) +
                  this.z * ( zaxis*zaxis  +  cos*(1f - zaxis*zaxis));
        this.x = x;
        this.y = y;
        this.z = z;
    }


    /**
     * Create a string representation of this point.
     * @return the String
     */
    public String toString() {
        return getClass().getName() + "[x=" + x + ", y=" + y +
                                      ", z=" + z + ", w=" + w + "]";
    }
}
