Added ball generation from support points in 3D.

This can be used for applying Emo Welzl smallest enclosing ball
algorithm in the Euclidean 3D case.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/math/trunk@1562571 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Luc Maisonobe 2014-01-29 20:18:32 +00:00
parent 8dd48742c3
commit 7ce5950963
6 changed files with 532 additions and 165 deletions

View File

@ -0,0 +1,154 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.euclidean.threed;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.math3.geometry.enclosing.EnclosingBall;
import org.apache.commons.math3.geometry.enclosing.SupportBallGenerator;
import org.apache.commons.math3.geometry.euclidean.twod.DiskGenerator;
import org.apache.commons.math3.geometry.euclidean.twod.Euclidean2D;
import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
import org.apache.commons.math3.util.MathArrays;
/** Class generating an enclosing ball from its support points.
* @version $Id$
* @since 3.3
*/
public class SphereGenerator implements SupportBallGenerator<Euclidean3D, Vector3D> {
/** {@inheritDoc} */
public EnclosingBall<Euclidean3D, Vector3D> ballOnSupport(final List<Vector3D> support) {
if (support.size() < 1) {
return new EnclosingBall<Euclidean3D, Vector3D>(Vector3D.ZERO, -1.0);
} else {
final Vector3D vA = support.get(0);
if (support.size() < 2) {
return new EnclosingBall<Euclidean3D, Vector3D>(vA, 0, vA);
} else {
final Vector3D vB = support.get(1);
if (support.size() < 3) {
return new EnclosingBall<Euclidean3D, Vector3D>(new Vector3D(0.5, vA, 0.5, vB),
0.5 * vA.distance(vB),
vA, vB);
} else {
final Vector3D vC = support.get(2);
if (support.size() < 4) {
// delegate to 2D disk generator
final Plane p = new Plane(vA, vB, vC,
1.0e-10 * (vA.getNorm1() + vB.getNorm1() + vC.getNorm1()));
final EnclosingBall<Euclidean2D, Vector2D> disk =
new DiskGenerator().ballOnSupport(Arrays.asList(p.toSubSpace(vA),
p.toSubSpace(vB),
p.toSubSpace(vC)));
// convert back to 3D
return new EnclosingBall<Euclidean3D, Vector3D>(p.toSpace(disk.getCenter()),
disk.getRadius(), vA, vB, vC);
} else {
final Vector3D vD = support.get(3);
// a sphere is 3D can be defined as:
// (1) (x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = r^2
// which can be written:
// (2) (x^2 + y^2 + z^2) - 2 x_0 x - 2 y_0 y - 2 z_0 z + (x_0^2 + y_0^2 + z_0^2 - r^2) = 0
// or simply:
// (3) (x^2 + y^2 + z^2) + a x + b y + c z + d = 0
// with sphere center coordinates -a/2, -b/2, -c/2
// If the sphere exists, a b, c and d are a non zero solution to
// [ (x^2 + y^2 + z^2) x y z 1 ] [ 1 ] [ 0 ]
// [ (xA^2 + yA^2 + zA^2) xA yA zA 1 ] [ a ] [ 0 ]
// [ (xB^2 + yB^2 + zB^2) xB yB zB 1 ] * [ b ] = [ 0 ]
// [ (xC^2 + yC^2 + zC^2) xC yC zC 1 ] [ c ] [ 0 ]
// [ (xD^2 + yD^2 + zD^2) xD yD zD 1 ] [ d ] [ 0 ]
// So the determinant of the matrix is zero. Computing this determinant
// by expanding it using the minors m_ij of first row leads to
// (4) m_11 (x^2 + y^2 + z^2) - m_12 x + m_13 y - m_14 z + m_15 = 0
// So by identifying equations (2) and (4) we get the coordinates
// of center as:
// x_0 = +m_12 / (2 m_11)
// y_0 = -m_13 / (2 m_11)
// z_0 = +m_14 / (2 m_11)
// Note that the minors m_11, m_12, m_13 and m_14 all have the last column
// filled with 1.0, hence simplifying the computation
final double[] c1 = new double[] {
vA.getNormSq(), vB.getNormSq(), vC.getNormSq(), vD.getNormSq()
};
final double[] c2 = new double[] {
vA.getX(), vB.getX(), vC.getX(), vD.getX()
};
final double[] c3 = new double[] {
vA.getY(), vB.getY(), vC.getY(), vD.getY()
};
final double[] c4 = new double[] {
vA.getZ(), vB.getZ(), vC.getZ(), vD.getZ()
};
final double m11 = minor(c2, c3, c4);
final double m12 = minor(c1, c3, c4);
final double m13 = minor(c1, c2, c4);
final double m14 = minor(c1, c2, c3);
final Vector3D center = new Vector3D(0.5 * m12 / m11, -0.5 * m13 / m11, 0.5 * m14 / m11);
return new EnclosingBall<Euclidean3D, Vector3D>(center, center.distance(vA),
vA, vB, vC, vD);
}
}
}
}
}
/** Compute a dimension 4 minor, when 4<sup>th</sup> column is known to be filled with 1.0.
* <p>
* The computation is performed using {@link MathArrays#linearCombination(double[], double[])
* high accuracy sum of products}, trying to avoid cancellations effect. This should reduce
* risks in case of near co-planar points.
* </p>
* @param c1 first column
* @param c2 second column
* @param c3 third column
* @return value of the minor computed to high accuracy
*/
private double minor(final double[] c1, final double[] c2, final double[] c3) {
final double m01 = c2[0] * c3[1];
final double m02 = c2[0] * c3[2];
final double m03 = c2[0] * c3[3];
final double m10 = c2[1] * c3[0];
final double m12 = c2[1] * c3[2];
final double m13 = c2[1] * c3[3];
final double m20 = c2[2] * c3[0];
final double m21 = c2[2] * c3[1];
final double m23 = c2[2] * c3[3];
final double m30 = c2[3] * c3[0];
final double m31 = c2[3] * c3[1];
final double m32 = c2[3] * c3[2];
return MathArrays.linearCombination(new double[] {
c1[2], c1[1], c1[3], -c1[1], -c1[3], -c1[2],
c1[0], c1[3], c1[2], -c1[3], -c1[0], -c1[2],
c1[1], c1[0], c1[3], -c1[0], -c1[3], -c1[1],
c1[0], c1[2], c1[1], -c1[2], -c1[0], -c1[1]
},
new double[] {
m13, m32, m21, m23, m12, m31,
m23, m02, m30, m20, m32, m03,
m03, m31, m10, m13, m01, m30,
m12, m01, m20, m10, m21, m02
});
}
}

View File

@ -1,69 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.euclidean.twod;
import java.util.List;
import org.apache.commons.math3.geometry.enclosing.EnclosingBall;
import org.apache.commons.math3.geometry.enclosing.SupportBallGenerator;
import org.apache.commons.math3.util.MathArrays;
/** Class generating an enclosing ball from its support points.
* @version $Id$
* @since 3.3
*/
public class BallGenerator implements SupportBallGenerator<Euclidean2D, Vector2D> {
/** {@inheritDoc} */
public EnclosingBall<Euclidean2D, Vector2D> ballOnSupport(final List<Vector2D> support) {
if (support.size() < 1) {
return new EnclosingBall<Euclidean2D, Vector2D>(Vector2D.ZERO, -1.0);
} else {
final Vector2D vA = support.get(0);
if (support.size() < 2) {
return new EnclosingBall<Euclidean2D, Vector2D>(vA, 0, vA);
} else {
final Vector2D vB = support.get(1);
if (support.size() < 3) {
return new EnclosingBall<Euclidean2D, Vector2D>(new Vector2D(0.5, vA, 0.5, vB),
0.5 * vA.distance(vB),
vA, vB);
} else {
final Vector2D vC = support.get(2);
final Vector2D bc = vB.subtract(vC);
final Vector2D ca = vC.subtract(vA);
final Vector2D ab = vA.subtract(vB);
final double vA2 = vA.getNormSq();
final double vB2 = vB.getNormSq();
final double vC2 = vC.getNormSq();
final double d = 2 * MathArrays.linearCombination(vA.getX(), bc.getY(),
vB.getX(), ca.getY(),
vC.getX(), ab.getY());
final Vector2D center = new Vector2D( MathArrays.linearCombination(vA2, bc.getY(),
vB2, ca.getY(),
vC2, ab.getY()) / d,
-MathArrays.linearCombination(vA2, bc.getX(),
vB2, ca.getX(),
vC2, ab.getX()) / d);
return new EnclosingBall<Euclidean2D, Vector2D>(center, center.distance(vA), vA, vB, vC);
}
}
}
}
}

View File

@ -0,0 +1,107 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.euclidean.twod;
import java.util.List;
import org.apache.commons.math3.geometry.enclosing.EnclosingBall;
import org.apache.commons.math3.geometry.enclosing.SupportBallGenerator;
import org.apache.commons.math3.util.MathArrays;
/** Class generating an enclosing ball from its support points.
* @version $Id$
* @since 3.3
*/
public class DiskGenerator implements SupportBallGenerator<Euclidean2D, Vector2D> {
/** {@inheritDoc} */
public EnclosingBall<Euclidean2D, Vector2D> ballOnSupport(final List<Vector2D> support) {
if (support.size() < 1) {
return new EnclosingBall<Euclidean2D, Vector2D>(Vector2D.ZERO, -1.0);
} else {
final Vector2D vA = support.get(0);
if (support.size() < 2) {
return new EnclosingBall<Euclidean2D, Vector2D>(vA, 0, vA);
} else {
final Vector2D vB = support.get(1);
if (support.size() < 3) {
return new EnclosingBall<Euclidean2D, Vector2D>(new Vector2D(0.5, vA, 0.5, vB),
0.5 * vA.distance(vB),
vA, vB);
} else {
final Vector2D vC = support.get(2);
// a disk is 2D can be defined as:
// (1) (x - x_0)^2 + (y - y_0)^2 = r^2
// which can be written:
// (2) (x^2 + y^2) - 2 x_0 x - 2 y_0 y + (x_0^2 + y_0^2 - r^2) = 0
// or simply:
// (3) (x^2 + y^2) + a x + b y + c= 0
// with disk center coordinates -a/2, -b/2
// If the sphere exists, a, b and c are a non zero solution to
// [ (x^2 + y^2 ) x y 1 ] [ 1 ] [ 0 ]
// [ (xA^2 + yA^2) xA yA 1 ] [ a ] [ 0 ]
// [ (xB^2 + yB^2) xB yB 1 ] * [ b ] = [ 0 ]
// [ (xC^2 + yC^2) xC yC 1 ] [ c ] [ 0 ]
// So the determinant of the matrix is zero. Computing this determinant
// by expanding it using the minors m_ij of first row leads to
// (4) m_11 (x^2 + y^2) - m_12 x + m_13 y - m_14 = 0
// So by identifying equations (2) and (4) we get the coordinates
// of center as:
// x_0 = +m_12 / (2 m_11)
// y_0 = -m_13 / (2 m_11)
// Note that the minors m_11, m_12 and m_13 all have the last column
// filled with 1.0, hence simplifying the computation
final double[] c1 = new double[] {
vA.getNormSq(), vB.getNormSq(), vC.getNormSq()
};
final double[] c2 = new double[] {
vA.getX(), vB.getX(), vC.getX()
};
final double[] c3 = new double[] {
vA.getY(), vB.getY(), vC.getY()
};
final double m11 = minor(c2, c3);
final double m12 = minor(c1, c3);
final double m13 = minor(c1, c2);
final Vector2D center = new Vector2D(0.5 * m12 / m11, -0.5 * m13 / m11);
return new EnclosingBall<Euclidean2D, Vector2D>(center, center.distance(vA), vA, vB, vC);
}
}
}
}
/** Compute a dimension 3 minor, when 3<sup>d</sup> column is known to be filled with 1.0.
* <p>
* The computation is performed using {@link MathArrays#linearCombination(double[], double[])
* high accuracy sum of products}, trying to avoid cancellations effect. This should reduce
* risks in case of near co-planar points.
* </p>
* @param c1 first column
* @param c2 second column
* @return value of the minor computed to high accuracy
*/
private double minor(final double[] c1, final double[] c2) {
return MathArrays.linearCombination(new double[] {
c1[0], c1[2], c1[1], -c1[2], -c1[0], -c1[1]
},
new double[] {
c2[1], c2[0], c2[2], c2[1], c2[2], c2[0]
});
}
}

View File

@ -0,0 +1,153 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.euclidean.threed;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.math3.geometry.enclosing.EnclosingBall;
import org.apache.commons.math3.random.RandomGenerator;
import org.apache.commons.math3.random.UnitSphereRandomVectorGenerator;
import org.apache.commons.math3.random.Well1024a;
import org.junit.Assert;
import org.junit.Test;
public class SphereGeneratorTest {
@Test
public void testSupport0Point() {
List<Vector3D> support = Arrays.asList(new Vector3D[0]);
EnclosingBall<Euclidean3D, Vector3D> sphere = new SphereGenerator().ballOnSupport(support);
Assert.assertTrue(sphere.getRadius() < 0);
Assert.assertEquals(0, sphere.getSupportSize());
Assert.assertEquals(0, sphere.getSupport().length);
}
@Test
public void testSupport1Point() {
List<Vector3D> support = Arrays.asList(new Vector3D(1, 2, 3));
EnclosingBall<Euclidean3D, Vector3D> sphere = new SphereGenerator().ballOnSupport(support);
Assert.assertEquals(0.0, sphere.getRadius(), 1.0e-10);
Assert.assertTrue(sphere.contains(support.get(0)));
Assert.assertTrue(sphere.contains(support.get(0), 0.5));
Assert.assertFalse(sphere.contains(new Vector3D(support.get(0).getX() + 0.1,
support.get(0).getY() + 0.1,
support.get(0).getZ() + 0.1),
0.001));
Assert.assertTrue(sphere.contains(new Vector3D(support.get(0).getX() + 0.1,
support.get(0).getY() + 0.1,
support.get(0).getZ() + 0.1),
0.5));
Assert.assertEquals(0, support.get(0).distance(sphere.getCenter()), 1.0e-10);
Assert.assertEquals(1, sphere.getSupportSize());
Assert.assertTrue(support.get(0) == sphere.getSupport()[0]);
}
@Test
public void testSupport2Points() {
List<Vector3D> support = Arrays.asList(new Vector3D(1, 0, 0),
new Vector3D(3, 0, 0));
EnclosingBall<Euclidean3D, Vector3D> sphere = new SphereGenerator().ballOnSupport(support);
Assert.assertEquals(1.0, sphere.getRadius(), 1.0e-10);
int i = 0;
for (Vector3D v : support) {
Assert.assertTrue(sphere.contains(v));
Assert.assertEquals(1.0, v.distance(sphere.getCenter()), 1.0e-10);
Assert.assertTrue(v == sphere.getSupport()[i++]);
}
Assert.assertTrue(sphere.contains(new Vector3D(2, 0.9, 0)));
Assert.assertFalse(sphere.contains(Vector3D.ZERO));
Assert.assertEquals(0.0, new Vector3D(2, 0, 0).distance(sphere.getCenter()), 1.0e-10);
Assert.assertEquals(2, sphere.getSupportSize());
}
@Test
public void testSupport3Points() {
List<Vector3D> support = Arrays.asList(new Vector3D(1, 0, 0),
new Vector3D(3, 0, 0),
new Vector3D(2, 2, 0));
EnclosingBall<Euclidean3D, Vector3D> sphere = new SphereGenerator().ballOnSupport(support);
Assert.assertEquals(5.0 / 4.0, sphere.getRadius(), 1.0e-10);
int i = 0;
for (Vector3D v : support) {
Assert.assertTrue(sphere.contains(v));
Assert.assertEquals(5.0 / 4.0, v.distance(sphere.getCenter()), 1.0e-10);
Assert.assertTrue(v == sphere.getSupport()[i++]);
}
Assert.assertTrue(sphere.contains(new Vector3D(2, 0.9, 0)));
Assert.assertFalse(sphere.contains(new Vector3D(0.9, 0, 0)));
Assert.assertFalse(sphere.contains(new Vector3D(3.1, 0, 0)));
Assert.assertTrue(sphere.contains(new Vector3D(2.0, -0.499, 0)));
Assert.assertFalse(sphere.contains(new Vector3D(2.0, -0.501, 0)));
Assert.assertTrue(sphere.contains(new Vector3D(2.0, 3.0 / 4.0, -1.249)));
Assert.assertFalse(sphere.contains(new Vector3D(2.0, 3.0 / 4.0, -1.251)));
Assert.assertEquals(0.0, new Vector3D(2.0, 3.0 / 4.0, 0).distance(sphere.getCenter()), 1.0e-10);
Assert.assertEquals(3, sphere.getSupportSize());
}
@Test
public void testSupport4Points() {
List<Vector3D> support = Arrays.asList(new Vector3D(17, 14, 18),
new Vector3D(11, 14, 22),
new Vector3D( 2, 22, 17),
new Vector3D(22, 11, -10));
EnclosingBall<Euclidean3D, Vector3D> sphere = new SphereGenerator().ballOnSupport(support);
Assert.assertEquals(25.0, sphere.getRadius(), 1.0e-10);
int i = 0;
for (Vector3D v : support) {
Assert.assertTrue(sphere.contains(v));
Assert.assertEquals(25.0, v.distance(sphere.getCenter()), 1.0e-10);
Assert.assertTrue(v == sphere.getSupport()[i++]);
}
Assert.assertTrue(sphere.contains (new Vector3D(-22.999, 2, 2)));
Assert.assertFalse(sphere.contains(new Vector3D(-23.001, 2, 2)));
Assert.assertTrue(sphere.contains (new Vector3D( 26.999, 2, 2)));
Assert.assertFalse(sphere.contains(new Vector3D( 27.001, 2, 2)));
Assert.assertTrue(sphere.contains (new Vector3D(2, -22.999, 2)));
Assert.assertFalse(sphere.contains(new Vector3D(2, -23.001, 2)));
Assert.assertTrue(sphere.contains (new Vector3D(2, 26.999, 2)));
Assert.assertFalse(sphere.contains(new Vector3D(2, 27.001, 2)));
Assert.assertTrue(sphere.contains (new Vector3D(2, 2, -22.999)));
Assert.assertFalse(sphere.contains(new Vector3D(2, 2, -23.001)));
Assert.assertTrue(sphere.contains (new Vector3D(2, 2, 26.999)));
Assert.assertFalse(sphere.contains(new Vector3D(2, 2, 27.001)));
Assert.assertEquals(0.0, new Vector3D(2.0, 2.0, 2.0).distance(sphere.getCenter()), 1.0e-10);
Assert.assertEquals(4, sphere.getSupportSize());
}
@Test
public void testRandom() {
final RandomGenerator random = new Well1024a(0xd015982e9f31ee04l);
final UnitSphereRandomVectorGenerator sr = new UnitSphereRandomVectorGenerator(3, random);
for (int i = 0; i < 500; ++i) {
double d = 25 * random.nextDouble();
double refRadius = 10 * random.nextDouble();
Vector3D refCenter = new Vector3D(d, new Vector3D(sr.nextVector()));
List<Vector3D> support = new ArrayList<Vector3D>();
for (int j = 0; j < 5; ++j) {
support.add(new Vector3D(1.0, refCenter, refRadius, new Vector3D(sr.nextVector())));
}
EnclosingBall<Euclidean3D, Vector3D> sphere = new SphereGenerator().ballOnSupport(support);
Assert.assertEquals(0.0, refCenter.distance(sphere.getCenter()), 4e-7 * refRadius);
Assert.assertEquals(refRadius, sphere.getRadius(), 1e-7 * refRadius);
}
}
}

View File

@ -1,96 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.euclidean.twod;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.math3.geometry.enclosing.EnclosingBall;
import org.junit.Assert;
import org.junit.Test;
public class BallGeneratorTest {
@Test
public void testSupport0Point() {
List<Vector2D> support = Arrays.asList(new Vector2D[0]);
EnclosingBall<Euclidean2D, Vector2D> ball = new BallGenerator().ballOnSupport(support);
Assert.assertTrue(ball.getRadius() < 0);
Assert.assertEquals(0, ball.getSupportSize());
Assert.assertEquals(0, ball.getSupport().length);
}
@Test
public void testSupport1Point() {
List<Vector2D> support = Arrays.asList(new Vector2D(1, 2));
EnclosingBall<Euclidean2D, Vector2D> ball = new BallGenerator().ballOnSupport(support);
Assert.assertEquals(0.0, ball.getRadius(), 1.0e-10);
Assert.assertTrue(ball.contains(support.get(0)));
Assert.assertTrue(ball.contains(support.get(0), 0.5));
Assert.assertFalse(ball.contains(new Vector2D(support.get(0).getX() + 0.1,
support.get(0).getY() - 0.1),
0.001));
Assert.assertTrue(ball.contains(new Vector2D(support.get(0).getX() + 0.1,
support.get(0).getY() - 0.1),
0.5));
Assert.assertEquals(0, support.get(0).distance(ball.getCenter()), 1.0e-10);
Assert.assertEquals(1, ball.getSupportSize());
Assert.assertTrue(support.get(0) == ball.getSupport()[0]);
}
@Test
public void testSupport2Points() {
List<Vector2D> support = Arrays.asList(new Vector2D(1, 0),
new Vector2D(3, 0));
EnclosingBall<Euclidean2D, Vector2D> ball = new BallGenerator().ballOnSupport(support);
Assert.assertEquals(1.0, ball.getRadius(), 1.0e-10);
int i = 0;
for (Vector2D v : support) {
Assert.assertTrue(ball.contains(v));
Assert.assertEquals(1.0, v.distance(ball.getCenter()), 1.0e-10);
Assert.assertTrue(v == ball.getSupport()[i++]);
}
Assert.assertTrue(ball.contains(new Vector2D(2, 0.9)));
Assert.assertFalse(ball.contains(Vector2D.ZERO));
Assert.assertEquals(0.0, new Vector2D(2, 0).distance(ball.getCenter()), 1.0e-10);
Assert.assertEquals(2, ball.getSupportSize());
}
@Test
public void testSupport3Points() {
List<Vector2D> support = Arrays.asList(new Vector2D(1, 0),
new Vector2D(3, 0),
new Vector2D(2, 2));
EnclosingBall<Euclidean2D, Vector2D> ball = new BallGenerator().ballOnSupport(support);
Assert.assertEquals(5.0 / 4.0, ball.getRadius(), 1.0e-10);
int i = 0;
for (Vector2D v : support) {
Assert.assertTrue(ball.contains(v));
Assert.assertEquals(5.0 / 4.0, v.distance(ball.getCenter()), 1.0e-10);
Assert.assertTrue(v == ball.getSupport()[i++]);
}
Assert.assertTrue(ball.contains(new Vector2D(2, 0.9)));
Assert.assertFalse(ball.contains(new Vector2D(0.9, 0)));
Assert.assertFalse(ball.contains(new Vector2D(3.1, 0)));
Assert.assertTrue(ball.contains(new Vector2D(2.0, -0.499)));
Assert.assertFalse(ball.contains(new Vector2D(2.0, -0.501)));
Assert.assertEquals(0.0, new Vector2D(2.0, 3.0 / 4.0).distance(ball.getCenter()), 1.0e-10);
Assert.assertEquals(3, ball.getSupportSize());
}
}

View File

@ -0,0 +1,118 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math3.geometry.euclidean.twod;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.math3.geometry.enclosing.EnclosingBall;
import org.apache.commons.math3.random.RandomGenerator;
import org.apache.commons.math3.random.UnitSphereRandomVectorGenerator;
import org.apache.commons.math3.random.Well1024a;
import org.junit.Assert;
import org.junit.Test;
public class DiskGeneratorTest {
@Test
public void testSupport0Point() {
List<Vector2D> support = Arrays.asList(new Vector2D[0]);
EnclosingBall<Euclidean2D, Vector2D> disk = new DiskGenerator().ballOnSupport(support);
Assert.assertTrue(disk.getRadius() < 0);
Assert.assertEquals(0, disk.getSupportSize());
Assert.assertEquals(0, disk.getSupport().length);
}
@Test
public void testSupport1Point() {
List<Vector2D> support = Arrays.asList(new Vector2D(1, 2));
EnclosingBall<Euclidean2D, Vector2D> disk = new DiskGenerator().ballOnSupport(support);
Assert.assertEquals(0.0, disk.getRadius(), 1.0e-10);
Assert.assertTrue(disk.contains(support.get(0)));
Assert.assertTrue(disk.contains(support.get(0), 0.5));
Assert.assertFalse(disk.contains(new Vector2D(support.get(0).getX() + 0.1,
support.get(0).getY() - 0.1),
0.001));
Assert.assertTrue(disk.contains(new Vector2D(support.get(0).getX() + 0.1,
support.get(0).getY() - 0.1),
0.5));
Assert.assertEquals(0, support.get(0).distance(disk.getCenter()), 1.0e-10);
Assert.assertEquals(1, disk.getSupportSize());
Assert.assertTrue(support.get(0) == disk.getSupport()[0]);
}
@Test
public void testSupport2Points() {
List<Vector2D> support = Arrays.asList(new Vector2D(1, 0),
new Vector2D(3, 0));
EnclosingBall<Euclidean2D, Vector2D> disk = new DiskGenerator().ballOnSupport(support);
Assert.assertEquals(1.0, disk.getRadius(), 1.0e-10);
int i = 0;
for (Vector2D v : support) {
Assert.assertTrue(disk.contains(v));
Assert.assertEquals(1.0, v.distance(disk.getCenter()), 1.0e-10);
Assert.assertTrue(v == disk.getSupport()[i++]);
}
Assert.assertTrue(disk.contains(new Vector2D(2, 0.9)));
Assert.assertFalse(disk.contains(Vector2D.ZERO));
Assert.assertEquals(0.0, new Vector2D(2, 0).distance(disk.getCenter()), 1.0e-10);
Assert.assertEquals(2, disk.getSupportSize());
}
@Test
public void testSupport3Points() {
List<Vector2D> support = Arrays.asList(new Vector2D(1, 0),
new Vector2D(3, 0),
new Vector2D(2, 2));
EnclosingBall<Euclidean2D, Vector2D> disk = new DiskGenerator().ballOnSupport(support);
Assert.assertEquals(5.0 / 4.0, disk.getRadius(), 1.0e-10);
int i = 0;
for (Vector2D v : support) {
Assert.assertTrue(disk.contains(v));
Assert.assertEquals(5.0 / 4.0, v.distance(disk.getCenter()), 1.0e-10);
Assert.assertTrue(v == disk.getSupport()[i++]);
}
Assert.assertTrue(disk.contains(new Vector2D(2, 0.9)));
Assert.assertFalse(disk.contains(new Vector2D(0.9, 0)));
Assert.assertFalse(disk.contains(new Vector2D(3.1, 0)));
Assert.assertTrue(disk.contains(new Vector2D(2.0, -0.499)));
Assert.assertFalse(disk.contains(new Vector2D(2.0, -0.501)));
Assert.assertEquals(0.0, new Vector2D(2.0, 3.0 / 4.0).distance(disk.getCenter()), 1.0e-10);
Assert.assertEquals(3, disk.getSupportSize());
}
@Test
public void testRandom() {
final RandomGenerator random = new Well1024a(0x12faa818373ffe90l);
final UnitSphereRandomVectorGenerator sr = new UnitSphereRandomVectorGenerator(2, random);
for (int i = 0; i < 500; ++i) {
double d = 25 * random.nextDouble();
double refRadius = 10 * random.nextDouble();
Vector2D refCenter = new Vector2D(d, new Vector2D(sr.nextVector()));
List<Vector2D> support = new ArrayList<Vector2D>();
for (int j = 0; j < 4; ++j) {
support.add(new Vector2D(1.0, refCenter, refRadius, new Vector2D(sr.nextVector())));
}
EnclosingBall<Euclidean2D, Vector2D> sphere = new DiskGenerator().ballOnSupport(support);
Assert.assertEquals(0.0, refCenter.distance(sphere.getCenter()), 3e-9 * refRadius);
Assert.assertEquals(refRadius, sphere.getRadius(), 7e-10 * refRadius);
}
}
}