1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * https://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.geometry.euclidean.twod; 23 24 import java.util.List; 25 26 import org.hipparchus.fraction.BigFraction; 27 import org.hipparchus.geometry.enclosing.EnclosingBall; 28 import org.hipparchus.geometry.enclosing.SupportBallGenerator; 29 import org.hipparchus.util.FastMath; 30 31 /** Class generating an enclosing ball from its support points. 32 */ 33 public class DiskGenerator implements SupportBallGenerator<Euclidean2D, Vector2D> { 34 35 /** Empty constructor. 36 * <p> 37 * This constructor is not strictly necessary, but it prevents spurious 38 * javadoc warnings with JDK 18 and later. 39 * </p> 40 * @since 3.0 41 */ 42 public DiskGenerator() { // NOPMD - unnecessary constructor added intentionally to make javadoc happy 43 // nothing to do 44 } 45 46 /** {@inheritDoc} */ 47 @Override 48 public EnclosingBall<Euclidean2D, Vector2D> ballOnSupport(final List<Vector2D> support) { 49 50 if (support.isEmpty()) { 51 return new EnclosingBall<>(Vector2D.ZERO, Double.NEGATIVE_INFINITY); 52 } else { 53 final Vector2D vA = support.get(0); 54 if (support.size() < 2) { 55 return new EnclosingBall<>(vA, 0, vA); 56 } else { 57 final Vector2D vB = support.get(1); 58 if (support.size() < 3) { 59 final Vector2D center = new Vector2D(0.5, vA, 0.5, vB); 60 61 // we could have computed r directly from the vA and vB 62 // (it was done this way up to Hipparchus 1.0), but as center 63 // is approximated in the computation above, it is better to 64 // take the final value of center and compute r from the distances 65 // to center of all support points, using a max to ensure all support 66 // points belong to the ball 67 // see <https://github.com/Hipparchus-Math/hipparchus/issues/20> 68 final double r = FastMath.max(Vector2D.distance(vA, center), 69 Vector2D.distance(vB, center)); 70 return new EnclosingBall<>(center, r, vA, vB); 71 72 } else { 73 final Vector2D vC = support.get(2); 74 // a disk is 2D can be defined as: 75 // (1) (x - x_0)^2 + (y - y_0)^2 = r^2 76 // which can be written: 77 // (2) (x^2 + y^2) - 2 x_0 x - 2 y_0 y + (x_0^2 + y_0^2 - r^2) = 0 78 // or simply: 79 // (3) (x^2 + y^2) + a x + b y + c = 0 80 // with disk center coordinates -a/2, -b/2 81 // If the disk exists, a, b and c are a non-zero solution to 82 // [ (x^2 + y^2 ) x y 1 ] [ 1 ] [ 0 ] 83 // [ (xA^2 + yA^2) xA yA 1 ] [ a ] [ 0 ] 84 // [ (xB^2 + yB^2) xB yB 1 ] * [ b ] = [ 0 ] 85 // [ (xC^2 + yC^2) xC yC 1 ] [ c ] [ 0 ] 86 // So the determinant of the matrix is zero. Computing this determinant 87 // by expanding it using the minors m_ij of first row leads to 88 // (4) m_11 (x^2 + y^2) - m_12 x + m_13 y - m_14 = 0 89 // So by identifying equations (2) and (4) we get the coordinates 90 // of center as: 91 // x_0 = +m_12 / (2 m_11) 92 // y_0 = -m_13 / (2 m_11) 93 // Note that the minors m_11, m_12 and m_13 all have the last column 94 // filled with 1.0, hence simplifying the computation 95 final BigFraction[] c2 = { 96 new BigFraction(vA.getX()), new BigFraction(vB.getX()), new BigFraction(vC.getX()) 97 }; 98 final BigFraction[] c3 = { 99 new BigFraction(vA.getY()), new BigFraction(vB.getY()), new BigFraction(vC.getY()) 100 }; 101 final BigFraction[] c1 = { 102 c2[0].multiply(c2[0]).add(c3[0].multiply(c3[0])), 103 c2[1].multiply(c2[1]).add(c3[1].multiply(c3[1])), 104 c2[2].multiply(c2[2]).add(c3[2].multiply(c3[2])) 105 }; 106 final BigFraction twoM11 = minor(c2, c3).multiply(2); 107 final BigFraction m12 = minor(c1, c3); 108 final BigFraction m13 = minor(c1, c2); 109 final Vector2D center = new Vector2D( m12.divide(twoM11).doubleValue(), 110 -m13.divide(twoM11).doubleValue()); 111 112 // we could have computed r directly from the minors above 113 // (it was done this way up to Hipparchus 1.0), but as center 114 // is approximated in the computation above, it is better to 115 // take the final value of center and compute r from the distances 116 // to center of all support points, using a max to ensure all support 117 // points belong to the ball 118 // see <https://github.com/Hipparchus-Math/hipparchus/issues/20> 119 final double r = FastMath.max(Vector2D.distance(vA, center), 120 FastMath.max(Vector2D.distance(vB, center), 121 Vector2D.distance(vC, center))); 122 return new EnclosingBall<>(center, r, vA, vB, vC); 123 124 } 125 } 126 } 127 } 128 129 /** Compute a dimension 3 minor, when 3<sup>d</sup> column is known to be filled with 1.0. 130 * @param c1 first column 131 * @param c2 second column 132 * @return value of the minor computed has an exact fraction 133 */ 134 private BigFraction minor(final BigFraction[] c1, final BigFraction[] c2) { 135 return c2[0].multiply(c1[2].subtract(c1[1])). 136 add(c2[1].multiply(c1[0].subtract(c1[2]))). 137 add(c2[2].multiply(c1[1].subtract(c1[0]))); 138 } 139 140 }