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
33package com.jme3.math;
34
35import com.jme3.export.*;
36import java.io.IOException;
37import 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 */
48public 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