All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.math.LineSegment Maven / Gradle / Ivy

There is a newer version: 3.7.0-stable
Show newest version
/*
 * Copyright (c) 2009-2020 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.*;
import com.jme3.util.TempVars;
import java.io.IOException;

/**
 * 

LineSegment represents a segment in the space. This is a portion of a Line * that has a limited start and end points.

*

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.

*

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.

* * @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; /** * Instantiate a zero-length segment at the origin. */ public LineSegment() { origin = new Vector3f(); direction = new Vector3f(); } /** * Instantiate a copy of the specified segment. * * @param ls the LineSegment to copy (not null, unaffected) */ public LineSegment(LineSegment ls) { this.origin = new Vector3f(ls.getOrigin()); this.direction = new Vector3f(ls.getDirection()); this.extent = ls.getExtent(); } /** *

Creates a new LineSegment with the given origin, direction and extent.

*

Note that the origin is not one of the ends of the LineSegment, but its center.

* * @param origin the location of the desired midpoint (alias created) * @param direction the desired direction vector (alias created) * @param extent the extent: 1/2 of the desired length, assuming direction * is a unit vector */ public LineSegment(Vector3f origin, Vector3f direction, float extent) { this.origin = origin; this.direction = direction; this.extent = extent; } /** *

Creates a new LineSegment with a given origin and end. This constructor will calculate the * center, the direction and the extent.

* * @param start location of the negative endpoint (not null, unaffected) * @param end location of the negative endpoint (not null, unaffected) */ 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(); } /** * Copy the specified segment to this one. * * @param ls the LineSegment to copy (not null, unaffected) */ public void set(LineSegment ls) { this.origin = new Vector3f(ls.getOrigin()); this.direction = new Vector3f(ls.getDirection()); this.extent = ls.getExtent(); } /** * Calculate the distance between this segment and the specified point. * * @param point a location vector (not null, unaffected) * @return the minimum distance (≥0) */ public float distance(Vector3f point) { return FastMath.sqrt(distanceSquared(point)); } /** * Calculate the distance between this segment and another. * * @param ls the other LineSegment (not null, unaffected) * @return the minimum distance (≥0) */ public float distance(LineSegment ls) { return FastMath.sqrt(distanceSquared(ls)); } /** * Calculate the distance between this segment and the specified Ray. * * @param r the input Ray (not null, unaffected) * @return the minimum distance (≥0) */ public float distance(Ray r) { return FastMath.sqrt(distanceSquared(r)); } /** * Calculate the squared distance between this segment and the specified * point. * * @param point location vector of the input point (not null, unaffected) * @return the square of the minimum distance (≥0) */ 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; } /** * Calculate the squared distance between this segment and another. * * @param test the other LineSegment (not null, unaffected) * @return the square of the minimum distance (≥0) */ 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); } /** * Calculate the squared distance between this segment and the specified * Ray. * * @param r the input Ray (not null, unaffected) * @return the square of the minimum distance (≥0) */ 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); } /** * Access the direction of this segment. * * @return the pre-existing direction vector */ public Vector3f getDirection() { return direction; } /** * Alter the direction of this segment. * * @param direction the desired direction vector (alias created!) */ public void setDirection(Vector3f direction) { this.direction = direction; } /** * Read the extent of this segment. * * @return the extent */ public float getExtent() { return extent; } /** * Alter the extent of this segment. * * @param extent the desired extent */ public void setExtent(float extent) { this.extent = extent; } /** * Access the origin of this segment. * * @return the pre-existing location vector */ public Vector3f getOrigin() { return origin; } /** * Alter the origin of this segment. * * @param origin the desired location vector (alias created!) */ public void setOrigin(Vector3f origin) { this.origin = origin; } /** * Determine the location of this segment's positive end. * * @param store storage for the result (modified if not null) * @return a location vector (either store or a new vector) */ public Vector3f getPositiveEnd(Vector3f store) { if (store == null) { store = new Vector3f(); } return origin.add((direction.mult(extent, store)), store); } /** * Determine the location of this segment's negative end. * * @param store storage for the result (modified if not null) * @return a location vector (either store or a new vector) */ public Vector3f getNegativeEnd(Vector3f store) { if (store == null) { store = new Vector3f(); } return origin.subtract((direction.mult(extent, store)), store); } /** * Serialize this segment to the specified exporter, for example when * saving to a J3O file. * * @param e (not null) * @throws IOException from the exporter */ @Override 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); } /** * De-serialize this segment from the specified importer, for example * when loading from a J3O file. * * @param importer (not null) * @throws IOException from the importer */ @Override public void read(JmeImporter importer) throws IOException { InputCapsule capsule = importer.getCapsule(this); origin = (Vector3f) capsule.readSavable("origin", Vector3f.ZERO.clone()); direction = (Vector3f) capsule.readSavable("direction", Vector3f.ZERO.clone()); extent = capsule.readFloat("extent", 0); } /** * Create a copy of this segment. * * @return a new instance, equivalent to this one */ @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(); } } /** *

Evaluates whether a given point is contained within the axis aligned bounding box * that contains this LineSegment.

This function is float error aware.

* * @param point the location of the input point (not null, unaffected) * @return true if contained in the box, otherwise false */ public boolean isPointInsideBounds(Vector3f point) { return isPointInsideBounds(point, Float.MIN_VALUE); } /** *

Evaluates whether a given point is contained within the axis aligned bounding box * that contains this LineSegment.

This function accepts an error parameter, which * is added to the extent of the bounding box.

* * @param point the location of the input point (not null, unaffected) * @param error the desired margin for error * @return true if contained in the box, otherwise false */ 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy