DiskGenerator.java

  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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.geometry.euclidean.twod;

  22. import java.util.List;

  23. import org.hipparchus.fraction.BigFraction;
  24. import org.hipparchus.geometry.enclosing.EnclosingBall;
  25. import org.hipparchus.geometry.enclosing.SupportBallGenerator;
  26. import org.hipparchus.util.FastMath;

  27. /** Class generating an enclosing ball from its support points.
  28.  */
  29. public class DiskGenerator implements SupportBallGenerator<Euclidean2D, Vector2D> {

  30.     /** Empty constructor.
  31.      * <p>
  32.      * This constructor is not strictly necessary, but it prevents spurious
  33.      * javadoc warnings with JDK 18 and later.
  34.      * </p>
  35.      * @since 3.0
  36.      */
  37.     public DiskGenerator() { // NOPMD - unnecessary constructor added intentionally to make javadoc happy
  38.         // nothing to do
  39.     }

  40.     /** {@inheritDoc} */
  41.     @Override
  42.     public EnclosingBall<Euclidean2D, Vector2D> ballOnSupport(final List<Vector2D> support) {

  43.         if (support.isEmpty()) {
  44.             return new EnclosingBall<>(Vector2D.ZERO, Double.NEGATIVE_INFINITY);
  45.         } else {
  46.             final Vector2D vA = support.get(0);
  47.             if (support.size() < 2) {
  48.                 return new EnclosingBall<>(vA, 0, vA);
  49.             } else {
  50.                 final Vector2D vB = support.get(1);
  51.                 if (support.size() < 3) {
  52.                     final Vector2D center = new Vector2D(0.5, vA, 0.5, vB);

  53.                     // we could have computed r directly from the vA and vB
  54.                     // (it was done this way up to Hipparchus 1.0), but as center
  55.                     // is approximated in the computation above, it is better to
  56.                     // take the final value of center and compute r from the distances
  57.                     // to center of all support points, using a max to ensure all support
  58.                     // points belong to the ball
  59.                     // see <https://github.com/Hipparchus-Math/hipparchus/issues/20>
  60.                     final double r = FastMath.max(Vector2D.distance(vA, center),
  61.                                                   Vector2D.distance(vB, center));
  62.                     return new EnclosingBall<>(center, r, vA, vB);

  63.                 } else {
  64.                     final Vector2D vC = support.get(2);
  65.                     // a disk is 2D can be defined as:
  66.                     // (1)   (x - x_0)^2 + (y - y_0)^2 = r^2
  67.                     // which can be written:
  68.                     // (2)   (x^2 + y^2) - 2 x_0 x - 2 y_0 y + (x_0^2 + y_0^2 - r^2) = 0
  69.                     // or simply:
  70.                     // (3)   (x^2 + y^2) + a x + b y + c = 0
  71.                     // with disk center coordinates -a/2, -b/2
  72.                     // If the disk exists, a, b and c are a non-zero solution to
  73.                     // [ (x^2  + y^2 )   x    y   1 ]   [ 1 ]   [ 0 ]
  74.                     // [ (xA^2 + yA^2)   xA   yA  1 ]   [ a ]   [ 0 ]
  75.                     // [ (xB^2 + yB^2)   xB   yB  1 ] * [ b ] = [ 0 ]
  76.                     // [ (xC^2 + yC^2)   xC   yC  1 ]   [ c ]   [ 0 ]
  77.                     // So the determinant of the matrix is zero. Computing this determinant
  78.                     // by expanding it using the minors m_ij of first row leads to
  79.                     // (4)   m_11 (x^2 + y^2) - m_12 x + m_13 y - m_14 = 0
  80.                     // So by identifying equations (2) and (4) we get the coordinates
  81.                     // of center as:
  82.                     //      x_0 = +m_12 / (2 m_11)
  83.                     //      y_0 = -m_13 / (2 m_11)
  84.                     // Note that the minors m_11, m_12 and m_13 all have the last column
  85.                     // filled with 1.0, hence simplifying the computation
  86.                     final BigFraction[] c2 = {
  87.                         new BigFraction(vA.getX()), new BigFraction(vB.getX()), new BigFraction(vC.getX())
  88.                     };
  89.                     final BigFraction[] c3 = {
  90.                         new BigFraction(vA.getY()), new BigFraction(vB.getY()), new BigFraction(vC.getY())
  91.                     };
  92.                     final BigFraction[] c1 = {
  93.                         c2[0].multiply(c2[0]).add(c3[0].multiply(c3[0])),
  94.                         c2[1].multiply(c2[1]).add(c3[1].multiply(c3[1])),
  95.                         c2[2].multiply(c2[2]).add(c3[2].multiply(c3[2]))
  96.                     };
  97.                     final BigFraction twoM11 = minor(c2, c3).multiply(2);
  98.                     final BigFraction m12    = minor(c1, c3);
  99.                     final BigFraction m13    = minor(c1, c2);
  100.                     final Vector2D center    = new Vector2D( m12.divide(twoM11).doubleValue(),
  101.                                                             -m13.divide(twoM11).doubleValue());

  102.                      // we could have computed r directly from the minors above
  103.                      // (it was done this way up to Hipparchus 1.0), but as center
  104.                      // is approximated in the computation above, it is better to
  105.                      // take the final value of center and compute r from the distances
  106.                      // to center of all support points, using a max to ensure all support
  107.                      // points belong to the ball
  108.                      // see <https://github.com/Hipparchus-Math/hipparchus/issues/20>
  109.                      final double r = FastMath.max(Vector2D.distance(vA, center),
  110.                                                    FastMath.max(Vector2D.distance(vB, center),
  111.                                                                 Vector2D.distance(vC, center)));
  112.                     return new EnclosingBall<>(center, r, vA, vB, vC);

  113.                 }
  114.             }
  115.         }
  116.     }

  117.     /** Compute a dimension 3 minor, when 3<sup>d</sup> column is known to be filled with 1.0.
  118.      * @param c1 first column
  119.      * @param c2 second column
  120.      * @return value of the minor computed has an exact fraction
  121.      */
  122.     private BigFraction minor(final BigFraction[] c1, final BigFraction[] c2) {
  123.         return      c2[0].multiply(c1[2].subtract(c1[1])).
  124.                 add(c2[1].multiply(c1[0].subtract(c1[2]))).
  125.                 add(c2[2].multiply(c1[1].subtract(c1[0])));
  126.     }

  127. }