Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement equals(), hashCode(), and isSimilar() for bounding volumes #2252

Merged
merged 10 commits into from
Jun 9, 2024
73 changes: 72 additions & 1 deletion jme3-core/src/main/java/com/jme3/bounding/BoundingBox.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* Copyright (c) 2009-2024 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -46,6 +46,7 @@
import java.io.IOException;
import java.nio.FloatBuffer;
//import com.jme.scene.TriMesh;
import java.util.Objects;

/**
* <code>BoundingBox</code> describes a bounding volume as an axis-aligned box.
Expand Down Expand Up @@ -587,6 +588,76 @@ public BoundingVolume clone(BoundingVolume store) {
return rVal;
}

/**
* Tests for exact equality with the argument, distinguishing -0 from 0. If
* {@code other} is null, false is returned. Either way, the current
* instance is unaffected.
*
* @param other the object to compare (may be null, unaffected)
* @return true if {@code this} and {@code other} have identical values,
* otherwise false
*/
@Override
public boolean equals(Object other) {
if (!(other instanceof BoundingBox)) {
return false;
}

if (this == other) {
return true;
}

BoundingBox otherBoundingBox = (BoundingBox) other;
if (Float.compare(xExtent, otherBoundingBox.xExtent) != 0) {
return false;
} else if (Float.compare(yExtent, otherBoundingBox.yExtent) != 0) {
return false;
} else if (Float.compare(zExtent, otherBoundingBox.zExtent) != 0) {
return false;
} else {
return super.equals(otherBoundingBox);
}
}

/**
* Returns a hash code. If two bounding boxes have identical values, they
* will have the same hash code. The current instance is unaffected.
*
* @return a 32-bit value for use in hashing
*/
@Override
public int hashCode() {
int hash = Objects.hash(xExtent, yExtent, zExtent);
hash = 59 * hash + super.hashCode();

return hash;
}

/**
* Tests for approximate equality with the specified bounding box, using the
* specified tolerance. If {@code other} is null, false is returned. Either
* way, the current instance is unaffected.
*
* @param aabb the bounding box to compare (unaffected) or null for none
* @param epsilon the tolerance for each component
* @return true if all components are within tolerance, otherwise false
*/
public boolean isSimilar(BoundingBox aabb, float epsilon) {
if (aabb == null) {
return false;
} else if (Float.compare(Math.abs(aabb.xExtent - xExtent), epsilon) > 0) {
return false;
} else if (Float.compare(Math.abs(aabb.yExtent - yExtent), epsilon) > 0) {
return false;
} else if (Float.compare(Math.abs(aabb.zExtent - zExtent), epsilon) > 0) {
return false;
} else if (!center.isSimilar(aabb.getCenter(), epsilon)) {
return false;
}
// The checkPlane field is ignored.
return true;
}

/**
* <code>toString</code> returns the string representation of this object.
* The form is: "[Center: vector xExtent: X.XX yExtent: Y.YY zExtent:
Expand Down
65 changes: 64 additions & 1 deletion jme3-core/src/main/java/com/jme3/bounding/BoundingSphere.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* Copyright (c) 2009-2024 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -43,6 +43,7 @@
import com.jme3.util.TempVars;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand Down Expand Up @@ -651,6 +652,68 @@ public BoundingVolume clone(BoundingVolume store) {
return new BoundingSphere(radius, center.clone());
}

/**
* Tests for exact equality with the argument, distinguishing -0 from 0. If
* {@code other} is null, false is returned. Either way, the current
* instance is unaffected.
*
* @param other the object to compare (may be null, unaffected)
* @return true if {@code this} and {@code other} have identical values,
* otherwise false
*/
@Override
public boolean equals(Object other) {
if (!(other instanceof BoundingSphere)) {
return false;
}

if (this == other) {
return true;
}

BoundingSphere otherBoundingSphere = (BoundingSphere) other;
if (Float.compare(radius, otherBoundingSphere.getRadius()) != 0) {
return false;
} else {
return super.equals(otherBoundingSphere);
}
}

/**
* Returns a hash code. If two bounding boxes have identical values, they
* will have the same hash code. The current instance is unaffected.
*
* @return a 32-bit value for use in hashing
*/
@Override
public int hashCode() {
int hash = Objects.hash(radius);
hash = 59 * hash + super.hashCode();

return hash;
}

/**
* Tests for approximate equality with the specified bounding sphere, using
* the specified tolerance. If {@code other} is null, false is returned.
* Either way, the current instance is unaffected.
*
* @param sphere the bounding sphere to compare (unaffected) or null for none
* @param epsilon the tolerance for each component
* @return true if all components are within tolerance, otherwise false
*/
public boolean isSimilar(BoundingSphere sphere, float epsilon) {
if (sphere == null) {
return false;
} else if (Float.compare(Math.abs(sphere.getRadius() - radius), epsilon) > 0) {
return false;
} else if (!center.isSimilar(sphere.getCenter(), epsilon)) {
return false;
}
// The checkPlane field is ignored.
return true;
}

/**
* <code>toString</code> returns the string representation of this object.
* The form is: "Radius: RRR.SSSS Center: vector".
Expand Down
45 changes: 44 additions & 1 deletion jme3-core/src/main/java/com/jme3/bounding/BoundingVolume.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2009-2021 jMonkeyEngine
* Copyright (c) 2009-2024 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -40,6 +40,7 @@
import com.jme3.util.TempVars;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.Objects;

/**
* <code>BoundingVolume</code> defines an interface for dealing with
Expand Down Expand Up @@ -180,6 +181,48 @@ public final BoundingVolume transform(Transform trans) {
*/
public abstract BoundingVolume clone(BoundingVolume store);

/**
* Tests for exact equality with the argument, distinguishing -0 from 0. If
* {@code other} is null, false is returned. Either way, the current
* instance is unaffected.
*
* @param other the object to compare (may be null, unaffected)
* @return true if {@code this} and {@code other} have identical values,
* otherwise false
*/
@Override
public boolean equals(Object other) {
if (!(other instanceof BoundingVolume)) {
return false;
}

if (this == other) {
return true;
}

BoundingVolume otherBoundingVolume = (BoundingVolume) other;
if (!center.equals(otherBoundingVolume.getCenter())) {
return false;
}
// The checkPlane field is ignored.

return true;
}

/**
* Returns a hash code. If two bounding volumes have identical values, they
* will have the same hash code. The current instance is unaffected.
*
* @return a 32-bit value for use in hashing
*/
@Override
public int hashCode() {
int hash = Objects.hash(center);
// The checkPlane field is ignored.

return hash;
}

public final Vector3f getCenter() {
return center;
}
Expand Down
99 changes: 99 additions & 0 deletions jme3-core/src/test/java/com/jme3/bounding/TestBoundingBox.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2024 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.bounding;

import com.jme3.math.Vector3f;
import org.junit.Assert;
import org.junit.Test;

/**
* Test cases for the BoundingBox class.
*
* @author Stephen Gold
*/
public class TestBoundingBox {
/**
* Verify that equals() behaves as expected.
*/
@Test
public void testEquals() {
BoundingBox bb1 = new BoundingBox(new Vector3f(3f, 4f, 5f), 0f, 1f, 2f);
BoundingBox bb2
= new BoundingBox(new Vector3f(3f, 4f, 5f), -0f, 1f, 2f);

BoundingBox bb3 = new BoundingBox(new Vector3f(3f, 0f, 2f), 9f, 8f, 7f);
BoundingBox bb4
= new BoundingBox(new Vector3f(3f, -0f, 2f), 9f, 8f, 7f);

BoundingBox bb5 = new BoundingBox(new Vector3f(4f, 5f, 6f), 9f, 8f, 7f);
BoundingBox bb6 = (BoundingBox) bb5.clone();
bb6.setCheckPlane(1);

// Clones are equal to their base instances:
Assert.assertEquals(bb1, bb1.clone());
Assert.assertEquals(bb2, bb2.clone());
Assert.assertEquals(bb3, bb3.clone());
Assert.assertEquals(bb4, bb4.clone());
Assert.assertEquals(bb5, bb5.clone());
Assert.assertEquals(bb6, bb6.clone());

Assert.assertNotEquals(bb1, bb2); // because their extents differ
Assert.assertNotEquals(bb3, bb4); // because their centers differ
Assert.assertEquals(bb5, bb6); // because check planes are ignored
}

/**
* Verify that isSimilar() behaves as expected.
*/
@Test
public void testIsSimilar() {
BoundingBox bb1 = new BoundingBox(new Vector3f(3f, 4f, 5f), 0f, 1f, 2f);
BoundingBox bb2
= new BoundingBox(new Vector3f(3f, 4f, 5f), 0f, 1.1f, 2f);

BoundingBox bb3 = new BoundingBox(new Vector3f(3f, 4f, 2f), 9f, 8f, 7f);
BoundingBox bb4
= new BoundingBox(new Vector3f(3f, 3.9f, 2f), 9f, 8f, 7f);

BoundingBox bb5 = new BoundingBox(new Vector3f(4f, 5f, 6f), 9f, 8f, 7f);
BoundingBox bb6 = (BoundingBox) bb5.clone();
bb6.setCheckPlane(1);

Assert.assertFalse(bb1.isSimilar(bb2, 0.09999f));
Assert.assertTrue(bb1.isSimilar(bb2, 0.10001f));

Assert.assertFalse(bb3.isSimilar(bb4, 0.09999f));
Assert.assertTrue(bb3.isSimilar(bb4, 0.10001f));

Assert.assertTrue(bb5.isSimilar(bb6, 0f)); // check planes are ignored
}
}
51 changes: 51 additions & 0 deletions jme3-core/src/test/java/com/jme3/bounding/TestBoundingSphere.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,57 @@
* @author Stephen Gold
*/
public class TestBoundingSphere {
/**
* Verify that equals() behaves as expected.
*/
@Test
public void testEquals() {
BoundingSphere bs1 = new BoundingSphere(0f, new Vector3f(3f, 4f, 5f));
BoundingSphere bs2 = new BoundingSphere(-0f, new Vector3f(3f, 4f, 5f));

BoundingSphere bs3 = new BoundingSphere(1f, new Vector3f(3f, 0f, 2f));
BoundingSphere bs4 = new BoundingSphere(1f, new Vector3f(3f, -0f, 2f));

BoundingSphere bs5 = new BoundingSphere(2f, new Vector3f(4f, 5f, 6f));
BoundingSphere bs6 = (BoundingSphere) bs5.clone();
bs6.setCheckPlane(1);

// Clones are equal to their base instances:
Assert.assertEquals(bs1, bs1.clone());
Assert.assertEquals(bs2, bs2.clone());
Assert.assertEquals(bs3, bs3.clone());
Assert.assertEquals(bs4, bs4.clone());
Assert.assertEquals(bs5, bs5.clone());
Assert.assertEquals(bs6, bs6.clone());

Assert.assertNotEquals(bs1, bs2); // because their radii differ
Assert.assertNotEquals(bs3, bs4); // because their centers differ
Assert.assertEquals(bs5, bs6); // because check planes are ignored
}

/**
* Verify that isSimilar() behaves as expected.
*/
@Test
public void testIsSimilar() {
BoundingSphere bs1 = new BoundingSphere(0f, new Vector3f(3f, 4f, 5f));
BoundingSphere bs2 = new BoundingSphere(0.1f, new Vector3f(3f, 4f, 5f));

BoundingSphere bs3 = new BoundingSphere(1f, new Vector3f(3f, 4f, 2f));
BoundingSphere bs4 = new BoundingSphere(1f, new Vector3f(3f, 3.9f, 2f));

BoundingSphere bs5 = new BoundingSphere(2f, new Vector3f(4f, 5f, 6f));
BoundingSphere bs6 = (BoundingSphere) bs5.clone();
bs6.setCheckPlane(1);

Assert.assertFalse(bs1.isSimilar(bs2, 0.09999f));
Assert.assertTrue(bs1.isSimilar(bs2, 0.10001f));

Assert.assertFalse(bs3.isSimilar(bs4, 0.09999f));
Assert.assertTrue(bs3.isSimilar(bs4, 0.10001f));

Assert.assertTrue(bs5.isSimilar(bs6, 0f)); // check planes are ignored
}

/**
* Verify that an infinite bounding sphere can be merged with a very
Expand Down
Loading