123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635 |
- /*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- package com.jme3.math;
- import com.jme3.export.InputCapsule;
- import com.jme3.export.JmeExporter;
- import com.jme3.export.JmeImporter;
- import com.jme3.export.OutputCapsule;
- import com.jme3.export.Savable;
- import com.jme3.util.TempVars;
- import java.io.IOException;
- /**
- * <p>LineSegment represents a segment in the space. This is a portion of a Line
- * that has a limited start and end points.</p>
- * <p>A LineSegment is defined by an origin, a direction and an extent (or length).
- * Direction should be a normalized vector. It is not internally normalized.</p>
- * <p>This class provides methods to calculate distances between LineSegments, Rays and Vectors.
- * It is also possible to retrieve both end points of the segment {@link LineSegment#getPositiveEnd(Vector3f)}
- * and {@link LineSegment#getNegativeEnd(Vector3f)}. There are also methods to check whether
- * a point is within the segment bounds.</p>
- *
- * @see Ray
- * @author Mark Powell
- * @author Joshua Slack
- */
- public class LineSegment implements Cloneable, Savable, java.io.Serializable {
- static final long serialVersionUID = 1;
- private Vector3f origin;
- private Vector3f direction;
- private float extent;
- public LineSegment() {
- origin = new Vector3f();
- direction = new Vector3f();
- }
- public LineSegment(LineSegment ls) {
- this.origin = new Vector3f(ls.getOrigin());
- this.direction = new Vector3f(ls.getDirection());
- this.extent = ls.getExtent();
- }
- /**
- * <p>Creates a new LineSegment with the given origin, direction and extent.</p>
- * <p>Note that the origin is not one of the ends of the LineSegment, but its center.</p>
- */
- public LineSegment(Vector3f origin, Vector3f direction, float extent) {
- this.origin = origin;
- this.direction = direction;
- this.extent = extent;
- }
- /**
- * <p>Creates a new LineSegment with a given origin and end. This constructor will calculate the
- * center, the direction and the extent.</p>
- */
- public LineSegment(Vector3f start, Vector3f end) {
- this.origin = new Vector3f(0.5f * (start.x + end.x), 0.5f * (start.y + end.y), 0.5f * (start.z + end.z));
- this.direction = end.subtract(start);
- this.extent = direction.length() * 0.5f;
- direction.normalizeLocal();
- }
- public void set(LineSegment ls) {
- this.origin = new Vector3f(ls.getOrigin());
- this.direction = new Vector3f(ls.getDirection());
- this.extent = ls.getExtent();
- }
- public float distance(Vector3f point) {
- return FastMath.sqrt(distanceSquared(point));
- }
- public float distance(LineSegment ls) {
- return FastMath.sqrt(distanceSquared(ls));
- }
- public float distance(Ray r) {
- return FastMath.sqrt(distanceSquared(r));
- }
- public float distanceSquared(Vector3f point) {
- TempVars vars = TempVars.get();
- Vector3f compVec1 = vars.vect1;
- point.subtract(origin, compVec1);
- float segmentParameter = direction.dot(compVec1);
- if (-extent < segmentParameter) {
- if (segmentParameter < extent) {
- origin.add(direction.mult(segmentParameter, compVec1),
- compVec1);
- } else {
- origin.add(direction.mult(extent, compVec1), compVec1);
- }
- } else {
- origin.subtract(direction.mult(extent, compVec1), compVec1);
- }
- compVec1.subtractLocal(point);
- float len = compVec1.lengthSquared();
- vars.release();
- return len;
- }
- public float distanceSquared(LineSegment test) {
- TempVars vars = TempVars.get();
- Vector3f compVec1 = vars.vect1;
- origin.subtract(test.getOrigin(), compVec1);
- float negativeDirectionDot = -(direction.dot(test.getDirection()));
- float diffThisDot = compVec1.dot(direction);
- float diffTestDot = -(compVec1.dot(test.getDirection()));
- float lengthOfDiff = compVec1.lengthSquared();
- vars.release();
- float determinant = FastMath.abs(1.0f - negativeDirectionDot
- * negativeDirectionDot);
- float s0, s1, squareDistance, extentDeterminant0, extentDeterminant1, tempS0, tempS1;
- if (determinant >= FastMath.FLT_EPSILON) {
- // segments are not parallel
- s0 = negativeDirectionDot * diffTestDot - diffThisDot;
- s1 = negativeDirectionDot * diffThisDot - diffTestDot;
- extentDeterminant0 = extent * determinant;
- extentDeterminant1 = test.getExtent() * determinant;
- if (s0 >= -extentDeterminant0) {
- if (s0 <= extentDeterminant0) {
- if (s1 >= -extentDeterminant1) {
- if (s1 <= extentDeterminant1) // region 0 (interior)
- {
- // minimum at two interior points of 3D lines
- float inverseDeterminant = ((float) 1.0)
- / determinant;
- s0 *= inverseDeterminant;
- s1 *= inverseDeterminant;
- squareDistance = s0
- * (s0 + negativeDirectionDot * s1 + (2.0f) * diffThisDot)
- + s1
- * (negativeDirectionDot * s0 + s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else // region 3 (side)
- {
- s1 = test.getExtent();
- tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
- if (tempS0 < -extent) {
- s0 = -extent;
- squareDistance = s0 * (s0 - (2.0f) * tempS0)
- + s1 * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else if (tempS0 <= extent) {
- s0 = tempS0;
- squareDistance = -s0 * s0 + s1
- * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else {
- s0 = extent;
- squareDistance = s0 * (s0 - (2.0f) * tempS0)
- + s1 * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- }
- }
- } else // region 7 (side)
- {
- s1 = -test.getExtent();
- tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
- if (tempS0 < -extent) {
- s0 = -extent;
- squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
- * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else if (tempS0 <= extent) {
- s0 = tempS0;
- squareDistance = -s0 * s0 + s1
- * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else {
- s0 = extent;
- squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
- * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- }
- }
- } else {
- if (s1 >= -extentDeterminant1) {
- if (s1 <= extentDeterminant1) // region 1 (side)
- {
- s0 = extent;
- tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
- if (tempS1 < -test.getExtent()) {
- s1 = -test.getExtent();
- squareDistance = s1 * (s1 - (2.0f) * tempS1)
- + s0 * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else if (tempS1 <= test.getExtent()) {
- s1 = tempS1;
- squareDistance = -s1 * s1 + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else {
- s1 = test.getExtent();
- squareDistance = s1 * (s1 - (2.0f) * tempS1)
- + s0 * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- }
- } else // region 2 (corner)
- {
- s1 = test.getExtent();
- tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
- if (tempS0 < -extent) {
- s0 = -extent;
- squareDistance = s0 * (s0 - (2.0f) * tempS0)
- + s1 * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else if (tempS0 <= extent) {
- s0 = tempS0;
- squareDistance = -s0 * s0 + s1
- * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else {
- s0 = extent;
- tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
- if (tempS1 < -test.getExtent()) {
- s1 = -test.getExtent();
- squareDistance = s1
- * (s1 - (2.0f) * tempS1) + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else if (tempS1 <= test.getExtent()) {
- s1 = tempS1;
- squareDistance = -s1 * s1 + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else {
- s1 = test.getExtent();
- squareDistance = s1
- * (s1 - (2.0f) * tempS1) + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- }
- }
- }
- } else // region 8 (corner)
- {
- s1 = -test.getExtent();
- tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
- if (tempS0 < -extent) {
- s0 = -extent;
- squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
- * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else if (tempS0 <= extent) {
- s0 = tempS0;
- squareDistance = -s0 * s0 + s1
- * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else {
- s0 = extent;
- tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
- if (tempS1 > test.getExtent()) {
- s1 = test.getExtent();
- squareDistance = s1 * (s1 - (2.0f) * tempS1)
- + s0 * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else if (tempS1 >= -test.getExtent()) {
- s1 = tempS1;
- squareDistance = -s1 * s1 + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else {
- s1 = -test.getExtent();
- squareDistance = s1 * (s1 - (2.0f) * tempS1)
- + s0 * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- }
- }
- }
- }
- } else {
- if (s1 >= -extentDeterminant1) {
- if (s1 <= extentDeterminant1) // region 5 (side)
- {
- s0 = -extent;
- tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
- if (tempS1 < -test.getExtent()) {
- s1 = -test.getExtent();
- squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else if (tempS1 <= test.getExtent()) {
- s1 = tempS1;
- squareDistance = -s1 * s1 + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else {
- s1 = test.getExtent();
- squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- }
- } else // region 4 (corner)
- {
- s1 = test.getExtent();
- tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
- if (tempS0 > extent) {
- s0 = extent;
- squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
- * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else if (tempS0 >= -extent) {
- s0 = tempS0;
- squareDistance = -s0 * s0 + s1
- * (s1 + (2.0f) * diffTestDot)
- + lengthOfDiff;
- } else {
- s0 = -extent;
- tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
- if (tempS1 < -test.getExtent()) {
- s1 = -test.getExtent();
- squareDistance = s1 * (s1 - (2.0f) * tempS1)
- + s0 * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else if (tempS1 <= test.getExtent()) {
- s1 = tempS1;
- squareDistance = -s1 * s1 + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else {
- s1 = test.getExtent();
- squareDistance = s1 * (s1 - (2.0f) * tempS1)
- + s0 * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- }
- }
- }
- } else // region 6 (corner)
- {
- s1 = -test.getExtent();
- tempS0 = -(negativeDirectionDot * s1 + diffThisDot);
- if (tempS0 > extent) {
- s0 = extent;
- squareDistance = s0 * (s0 - (2.0f) * tempS0) + s1
- * (s1 + (2.0f) * diffTestDot) + lengthOfDiff;
- } else if (tempS0 >= -extent) {
- s0 = tempS0;
- squareDistance = -s0 * s0 + s1
- * (s1 + (2.0f) * diffTestDot) + lengthOfDiff;
- } else {
- s0 = -extent;
- tempS1 = -(negativeDirectionDot * s0 + diffTestDot);
- if (tempS1 < -test.getExtent()) {
- s1 = -test.getExtent();
- squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else if (tempS1 <= test.getExtent()) {
- s1 = tempS1;
- squareDistance = -s1 * s1 + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- } else {
- s1 = test.getExtent();
- squareDistance = s1 * (s1 - (2.0f) * tempS1) + s0
- * (s0 + (2.0f) * diffThisDot)
- + lengthOfDiff;
- }
- }
- }
- }
- } else {
- // The segments are parallel. The average b0 term is designed to
- // ensure symmetry of the function. That is, dist(seg0,seg1) and
- // dist(seg1,seg0) should produce the same number.get
- float extentSum = extent + test.getExtent();
- float sign = (negativeDirectionDot > 0.0f ? -1.0f : 1.0f);
- float averageB0 = (0.5f) * (diffThisDot - sign * diffTestDot);
- float lambda = -averageB0;
- if (lambda < -extentSum) {
- lambda = -extentSum;
- } else if (lambda > extentSum) {
- lambda = extentSum;
- }
- squareDistance = lambda * (lambda + (2.0f) * averageB0)
- + lengthOfDiff;
- }
- return FastMath.abs(squareDistance);
- }
- public float distanceSquared(Ray r) {
- Vector3f kDiff = r.getOrigin().subtract(origin);
- float fA01 = -r.getDirection().dot(direction);
- float fB0 = kDiff.dot(r.getDirection());
- float fB1 = -kDiff.dot(direction);
- float fC = kDiff.lengthSquared();
- float fDet = FastMath.abs(1.0f - fA01 * fA01);
- float fS0, fS1, fSqrDist, fExtDet;
- if (fDet >= FastMath.FLT_EPSILON) {
- // The ray and segment are not parallel.
- fS0 = fA01 * fB1 - fB0;
- fS1 = fA01 * fB0 - fB1;
- fExtDet = extent * fDet;
- if (fS0 >= (float) 0.0) {
- if (fS1 >= -fExtDet) {
- if (fS1 <= fExtDet) // region 0
- {
- // minimum at interior points of ray and segment
- float fInvDet = ((float) 1.0) / fDet;
- fS0 *= fInvDet;
- fS1 *= fInvDet;
- fSqrDist = fS0
- * (fS0 + fA01 * fS1 + ((float) 2.0) * fB0)
- + fS1
- * (fA01 * fS0 + fS1 + ((float) 2.0) * fB1) + fC;
- } else // region 1
- {
- fS1 = extent;
- fS0 = -(fA01 * fS1 + fB0);
- if (fS0 > (float) 0.0) {
- fSqrDist = -fS0 * fS0 + fS1
- * (fS1 + ((float) 2.0) * fB1) + fC;
- } else {
- fS0 = (float) 0.0;
- fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
- }
- }
- } else // region 5
- {
- fS1 = -extent;
- fS0 = -(fA01 * fS1 + fB0);
- if (fS0 > (float) 0.0) {
- fSqrDist = -fS0 * fS0 + fS1
- * (fS1 + ((float) 2.0) * fB1) + fC;
- } else {
- fS0 = (float) 0.0;
- fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
- }
- }
- } else {
- if (fS1 <= -fExtDet) // region 4
- {
- fS0 = -(-fA01 * extent + fB0);
- if (fS0 > (float) 0.0) {
- fS1 = -extent;
- fSqrDist = -fS0 * fS0 + fS1
- * (fS1 + ((float) 2.0) * fB1) + fC;
- } else {
- fS0 = (float) 0.0;
- fS1 = -fB1;
- if (fS1 < -extent) {
- fS1 = -extent;
- } else if (fS1 > extent) {
- fS1 = extent;
- }
- fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
- }
- } else if (fS1 <= fExtDet) // region 3
- {
- fS0 = (float) 0.0;
- fS1 = -fB1;
- if (fS1 < -extent) {
- fS1 = -extent;
- } else if (fS1 > extent) {
- fS1 = extent;
- }
- fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
- } else // region 2
- {
- fS0 = -(fA01 * extent + fB0);
- if (fS0 > (float) 0.0) {
- fS1 = extent;
- fSqrDist = -fS0 * fS0 + fS1
- * (fS1 + ((float) 2.0) * fB1) + fC;
- } else {
- fS0 = (float) 0.0;
- fS1 = -fB1;
- if (fS1 < -extent) {
- fS1 = -extent;
- } else if (fS1 > extent) {
- fS1 = extent;
- }
- fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
- }
- }
- }
- } else {
- // ray and segment are parallel
- if (fA01 > (float) 0.0) {
- // opposite direction vectors
- fS1 = -extent;
- } else {
- // same direction vectors
- fS1 = extent;
- }
- fS0 = -(fA01 * fS1 + fB0);
- if (fS0 > (float) 0.0) {
- fSqrDist = -fS0 * fS0 + fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
- } else {
- fS0 = (float) 0.0;
- fSqrDist = fS1 * (fS1 + ((float) 2.0) * fB1) + fC;
- }
- }
- return FastMath.abs(fSqrDist);
- }
- public Vector3f getDirection() {
- return direction;
- }
- public void setDirection(Vector3f direction) {
- this.direction = direction;
- }
- public float getExtent() {
- return extent;
- }
- public void setExtent(float extent) {
- this.extent = extent;
- }
- public Vector3f getOrigin() {
- return origin;
- }
- public void setOrigin(Vector3f origin) {
- this.origin = origin;
- }
- // P+e*D
- public Vector3f getPositiveEnd(Vector3f store) {
- if (store == null) {
- store = new Vector3f();
- }
- return origin.add((direction.mult(extent, store)), store);
- }
- // P-e*D
- public Vector3f getNegativeEnd(Vector3f store) {
- if (store == null) {
- store = new Vector3f();
- }
- return origin.subtract((direction.mult(extent, store)), store);
- }
- public void write(JmeExporter e) throws IOException {
- OutputCapsule capsule = e.getCapsule(this);
- capsule.write(origin, "origin", Vector3f.ZERO);
- capsule.write(direction, "direction", Vector3f.ZERO);
- capsule.write(extent, "extent", 0);
- }
- public void read(JmeImporter e) throws IOException {
- InputCapsule capsule = e.getCapsule(this);
- origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone());
- direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone());
- extent = capsule.readFloat("extent", 0);
- }
- @Override
- public LineSegment clone() {
- try {
- LineSegment segment = (LineSegment) super.clone();
- segment.direction = direction.clone();
- segment.origin = origin.clone();
- return segment;
- } catch (CloneNotSupportedException e) {
- throw new AssertionError();
- }
- }
- /**
- * <p>Evaluates whether a given point is contained within the axis aligned bounding box
- * that contains this LineSegment.</p><p>This function is float error aware.</p>
- */
- public boolean isPointInsideBounds(Vector3f point) {
- return isPointInsideBounds(point, Float.MIN_VALUE);
- }
- /**
- * <p>Evaluates whether a given point is contained within the axis aligned bounding box
- * that contains this LineSegment.</p><p>This function accepts an error parameter, which
- * is added to the extent of the bounding box.</p>
- */
- public boolean isPointInsideBounds(Vector3f point, float error) {
- if (FastMath.abs(point.x - origin.x) > FastMath.abs(direction.x * extent) + error) {
- return false;
- }
- if (FastMath.abs(point.y - origin.y) > FastMath.abs(direction.y * extent) + error) {
- return false;
- }
- if (FastMath.abs(point.z - origin.z) > FastMath.abs(direction.z * extent) + error) {
- return false;
- }
- return true;
- }
- }
|