Home | History | Annotate | Download | only in math
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 package com.jme3.math;
     34 
     35 import com.jme3.export.*;
     36 import java.io.IOException;
     37 import java.util.logging.Logger;
     38 
     39 /**
     40  * <code>Plane</code> defines a plane where Normal dot (x,y,z) = Constant.
     41  * This provides methods for calculating a "distance" of a point from this
     42  * plane. The distance is pseudo due to the fact that it can be negative if the
     43  * point is on the non-normal side of the plane.
     44  *
     45  * @author Mark Powell
     46  * @author Joshua Slack
     47  */
     48 public class Plane implements Savable, Cloneable, java.io.Serializable {
     49 
     50     static final long serialVersionUID = 1;
     51 
     52     private static final Logger logger = Logger
     53             .getLogger(Plane.class.getName());
     54 
     55     public static enum Side {
     56         None,
     57         Positive,
     58         Negative
     59     }
     60 
     61     /**
     62      * Vector normal to the plane.
     63      */
     64     protected Vector3f normal = new Vector3f();
     65 
     66     /**
     67      * Constant of the plane. See formula in class definition.
     68      */
     69     protected float constant;
     70 
     71     /**
     72      * Constructor instantiates a new <code>Plane</code> object. This is the
     73      * default object and contains a normal of (0,0,0) and a constant of 0.
     74      */
     75     public Plane() {
     76     }
     77 
     78     /**
     79      * Constructor instantiates a new <code>Plane</code> object. The normal
     80      * and constant values are set at creation.
     81      *
     82      * @param normal
     83      *            the normal of the plane.
     84      * @param constant
     85      *            the constant of the plane.
     86      */
     87     public Plane(Vector3f normal, float constant) {
     88         if (normal == null) {
     89             throw new IllegalArgumentException("normal cannot be null");
     90         }
     91 
     92         this.normal.set(normal);
     93         this.constant = constant;
     94     }
     95 
     96     /**
     97      * <code>setNormal</code> sets the normal of the plane.
     98      *
     99      * @param normal
    100      *            the new normal of the plane.
    101      */
    102     public void setNormal(Vector3f normal) {
    103         if (normal == null) {
    104             throw new IllegalArgumentException("normal cannot be null");
    105         }
    106         this.normal.set(normal);
    107     }
    108 
    109     /**
    110      * <code>setNormal</code> sets the normal of the plane.
    111      *
    112      */
    113     public void setNormal(float x, float y, float z) {
    114         this.normal.set(x,y,z);
    115     }
    116 
    117     /**
    118      * <code>getNormal</code> retrieves the normal of the plane.
    119      *
    120      * @return the normal of the plane.
    121      */
    122     public Vector3f getNormal() {
    123         return normal;
    124     }
    125 
    126     /**
    127      * <code>setConstant</code> sets the constant value that helps define the
    128      * plane.
    129      *
    130      * @param constant
    131      *            the new constant value.
    132      */
    133     public void setConstant(float constant) {
    134         this.constant = constant;
    135     }
    136 
    137     /**
    138      * <code>getConstant</code> returns the constant of the plane.
    139      *
    140      * @return the constant of the plane.
    141      */
    142     public float getConstant() {
    143         return constant;
    144     }
    145 
    146     public Vector3f getClosestPoint(Vector3f point, Vector3f store){
    147 //        float t = constant - normal.dot(point);
    148 //        return store.set(normal).multLocal(t).addLocal(point);
    149         float t = (constant - normal.dot(point)) / normal.dot(normal);
    150         return store.set(normal).multLocal(t).addLocal(point);
    151     }
    152 
    153     public Vector3f getClosestPoint(Vector3f point){
    154         return getClosestPoint(point, new Vector3f());
    155     }
    156 
    157     public Vector3f reflect(Vector3f point, Vector3f store){
    158         if (store == null)
    159             store = new Vector3f();
    160 
    161         float d = pseudoDistance(point);
    162         store.set(normal).negateLocal().multLocal(d * 2f);
    163         store.addLocal(point);
    164         return store;
    165     }
    166 
    167     /**
    168      * <code>pseudoDistance</code> calculates the distance from this plane to
    169      * a provided point. If the point is on the negative side of the plane the
    170      * distance returned is negative, otherwise it is positive. If the point is
    171      * on the plane, it is zero.
    172      *
    173      * @param point
    174      *            the point to check.
    175      * @return the signed distance from the plane to a point.
    176      */
    177     public float pseudoDistance(Vector3f point) {
    178         return normal.dot(point) - constant;
    179     }
    180 
    181     /**
    182      * <code>whichSide</code> returns the side at which a point lies on the
    183      * plane. The positive values returned are: NEGATIVE_SIDE, POSITIVE_SIDE and
    184      * NO_SIDE.
    185      *
    186      * @param point
    187      *            the point to check.
    188      * @return the side at which the point lies.
    189      */
    190     public Side whichSide(Vector3f point) {
    191         float dis = pseudoDistance(point);
    192         if (dis < 0) {
    193             return Side.Negative;
    194         } else if (dis > 0) {
    195             return Side.Positive;
    196         } else {
    197             return Side.None;
    198         }
    199     }
    200 
    201     public boolean isOnPlane(Vector3f point){
    202         float dist = pseudoDistance(point);
    203         if (dist < FastMath.FLT_EPSILON && dist > -FastMath.FLT_EPSILON)
    204             return true;
    205         else
    206             return false;
    207     }
    208 
    209     /**
    210      * Initialize this plane using the three points of the given triangle.
    211      *
    212      * @param t
    213      *            the triangle
    214      */
    215     public void setPlanePoints(AbstractTriangle t) {
    216         setPlanePoints(t.get1(), t.get2(), t.get3());
    217     }
    218 
    219     /**
    220      * Initialize this plane using a point of origin and a normal.
    221      *
    222      * @param origin
    223      * @param normal
    224      */
    225     public void setOriginNormal(Vector3f origin, Vector3f normal){
    226         this.normal.set(normal);
    227         this.constant = normal.x * origin.x + normal.y * origin.y + normal.z * origin.z;
    228     }
    229 
    230     /**
    231      * Initialize the Plane using the given 3 points as coplanar.
    232      *
    233      * @param v1
    234      *            the first point
    235      * @param v2
    236      *            the second point
    237      * @param v3
    238      *            the third point
    239      */
    240     public void setPlanePoints(Vector3f v1, Vector3f v2, Vector3f v3) {
    241         normal.set(v2).subtractLocal(v1);
    242         normal.crossLocal(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z)
    243                 .normalizeLocal();
    244         constant = normal.dot(v1);
    245     }
    246 
    247     /**
    248      * <code>toString</code> returns a string thta represents the string
    249      * representation of this plane. It represents the normal as a
    250      * <code>Vector3f</code> object, so the format is the following:
    251      * com.jme.math.Plane [Normal: org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY,
    252      * Z=ZZ.ZZZZ] - Constant: CC.CCCCC]
    253      *
    254      * @return the string representation of this plane.
    255      */
    256     @Override
    257     public String toString() {
    258         return getClass().getSimpleName() + " [Normal: " + normal + " - Constant: "
    259                 + constant + "]";
    260     }
    261 
    262     public void write(JmeExporter e) throws IOException {
    263         OutputCapsule capsule = e.getCapsule(this);
    264         capsule.write(normal, "normal", Vector3f.ZERO);
    265         capsule.write(constant, "constant", 0);
    266     }
    267 
    268     public void read(JmeImporter e) throws IOException {
    269         InputCapsule capsule = e.getCapsule(this);
    270         normal = (Vector3f) capsule.readSavable("normal", Vector3f.ZERO.clone());
    271         constant = capsule.readFloat("constant", 0);
    272     }
    273 
    274     @Override
    275     public Plane clone() {
    276         try {
    277             Plane p = (Plane) super.clone();
    278             p.normal = normal.clone();
    279             return p;
    280         } catch (CloneNotSupportedException e) {
    281             throw new AssertionError();
    282         }
    283     }
    284 }
    285