Circle.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.spherical.twod;

  22. import org.hipparchus.exception.MathIllegalArgumentException;
  23. import org.hipparchus.geometry.euclidean.threed.Rotation;
  24. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  25. import org.hipparchus.geometry.partitioning.Embedding;
  26. import org.hipparchus.geometry.partitioning.Hyperplane;
  27. import org.hipparchus.geometry.partitioning.RegionFactory;
  28. import org.hipparchus.geometry.partitioning.Transform;
  29. import org.hipparchus.geometry.spherical.oned.Arc;
  30. import org.hipparchus.geometry.spherical.oned.ArcsSet;
  31. import org.hipparchus.geometry.spherical.oned.LimitAngle;
  32. import org.hipparchus.geometry.spherical.oned.S1Point;
  33. import org.hipparchus.geometry.spherical.oned.Sphere1D;
  34. import org.hipparchus.geometry.spherical.oned.SubLimitAngle;
  35. import org.hipparchus.util.FastMath;
  36. import org.hipparchus.util.MathUtils;
  37. import org.hipparchus.util.SinCos;

  38. /** This class represents an oriented great circle on the 2-sphere.

  39.  * <p>An oriented circle can be defined by a center point. The circle
  40.  * is the set of points that are in the normal plan the center.</p>

  41.  * <p>Since it is oriented the two spherical caps at its two sides are
  42.  * unambiguously identified as a left cap and a right cap. This can be
  43.  * used to identify the interior and the exterior in a simple way by
  44.  * local properties only when part of a line is used to define part of
  45.  * a spherical polygon boundary.</p>

  46.  */
  47. public class Circle
  48.         implements Hyperplane<Sphere2D, S2Point, Circle, SubCircle>,
  49.                    Embedding<Sphere2D, S2Point, Sphere1D, S1Point> {

  50.     /** Pole or circle center. */
  51.     private Vector3D pole;

  52.     /** First axis in the equator plane, origin of the phase angles. */
  53.     private Vector3D x;

  54.     /** Second axis in the equator plane, in quadrature with respect to x. */
  55.     private Vector3D y;

  56.     /** Tolerance below which close sub-arcs are merged together. */
  57.     private final double tolerance;

  58.     /** Build a great circle from its pole.
  59.      * <p>The circle is oriented in the trigonometric direction around pole.</p>
  60.      * @param pole circle pole
  61.      * @param tolerance tolerance below which close sub-arcs are merged together
  62.      * @exception MathIllegalArgumentException if tolerance is smaller than {@link Sphere1D#SMALLEST_TOLERANCE}
  63.      */
  64.     public Circle(final Vector3D pole, final double tolerance)
  65.         throws MathIllegalArgumentException {
  66.         Sphere2D.checkTolerance(tolerance);
  67.         reset(pole);
  68.         this.tolerance = tolerance;
  69.     }

  70.     /** Build a great circle from two non-aligned points.
  71.      * <p>The circle is oriented from first to second point using the path smaller than π.</p>
  72.      * @param first first point contained in the great circle
  73.      * @param second second point contained in the great circle
  74.      * @param tolerance tolerance below which close sub-arcs are merged together
  75.      * @exception MathIllegalArgumentException if tolerance is smaller than {@link Sphere1D#SMALLEST_TOLERANCE}
  76.      */
  77.     public Circle(final S2Point first, final S2Point second, final double tolerance)
  78.         throws MathIllegalArgumentException {
  79.         Sphere2D.checkTolerance(tolerance);
  80.         reset(first.getVector().crossProduct(second.getVector()));
  81.         this.tolerance = tolerance;
  82.     }

  83.     /** Build a circle from its internal components.
  84.      * <p>The circle is oriented in the trigonometric direction around center.</p>
  85.      * @param pole circle pole
  86.      * @param x first axis in the equator plane
  87.      * @param y second axis in the equator plane
  88.      * @param tolerance tolerance below which close sub-arcs are merged together
  89.      * @exception MathIllegalArgumentException if tolerance is smaller than {@link Sphere1D#SMALLEST_TOLERANCE}
  90.      */
  91.     private Circle(final Vector3D pole, final Vector3D x, final Vector3D y, final double tolerance)
  92.         throws MathIllegalArgumentException {
  93.         Sphere2D.checkTolerance(tolerance);
  94.         this.pole      = pole;
  95.         this.x         = x;
  96.         this.y         = y;
  97.         this.tolerance = tolerance;
  98.     }

  99.     /** Copy constructor.
  100.      * <p>The created instance is completely independent from the
  101.      * original instance, it is a deep copy.</p>
  102.      * @param circle circle to copy
  103.      */
  104.     public Circle(final Circle circle) {
  105.         this(circle.pole, circle.x, circle.y, circle.tolerance);
  106.     }

  107.     /** {@inheritDoc} */
  108.     @Override
  109.     public Circle copySelf() {
  110.         return new Circle(this);
  111.     }

  112.     /** Reset the instance as if built from a pole.
  113.      * <p>The circle is oriented in the trigonometric direction around pole.</p>
  114.      * @param newPole circle pole
  115.      */
  116.     public void reset(final Vector3D newPole) {
  117.         this.pole = newPole.normalize();
  118.         this.x    = newPole.orthogonal();
  119.         this.y    = Vector3D.crossProduct(newPole, x).normalize();
  120.     }

  121.     /** Revert the instance.
  122.      */
  123.     public void revertSelf() {
  124.         // x remains the same
  125.         y    = y.negate();
  126.         pole = pole.negate();
  127.     }

  128.     /** Get the reverse of the instance.
  129.      * <p>Get a circle with reversed orientation with respect to the
  130.      * instance. A new object is built, the instance is untouched.</p>
  131.      * @return a new circle, with orientation opposite to the instance orientation
  132.      */
  133.     public Circle getReverse() {
  134.         return new Circle(pole.negate(), x, y.negate(), tolerance);
  135.     }

  136.     /** {@inheritDoc} */
  137.     @Override
  138.     public S2Point project(S2Point point) {
  139.         return toSpace(toSubSpace(point));
  140.     }

  141.     /** {@inheritDoc} */
  142.     @Override
  143.     public double getTolerance() {
  144.         return tolerance;
  145.     }

  146.     /** {@inheritDoc}
  147.      * @see #getPhase(Vector3D)
  148.      */
  149.     @Override
  150.     public S1Point toSubSpace(final S2Point point) {
  151.         return new S1Point(getPhase(point.getVector()));
  152.     }

  153.     /** Get the phase angle of a direction.
  154.      * <p>
  155.      * The direction may not belong to the circle as the
  156.      * phase is computed for the meridian plane between the circle
  157.      * pole and the direction.
  158.      * </p>
  159.      * @param direction direction for which phase is requested
  160.      * @return phase angle of the direction around the circle
  161.      * @see #toSubSpace(S2Point)
  162.      */
  163.     public double getPhase(final Vector3D direction) {
  164.         return FastMath.PI + FastMath.atan2(-direction.dotProduct(y), -direction.dotProduct(x));
  165.     }

  166.     /** {@inheritDoc}
  167.      * @see #getPointAt(double)
  168.      */
  169.     @Override
  170.     public S2Point toSpace(final S1Point point) {
  171.         return new S2Point(getPointAt(point.getAlpha()));
  172.     }

  173.     /** Get a circle point from its phase around the circle.
  174.      * @param alpha phase around the circle
  175.      * @return circle point on the sphere
  176.      * @see #toSpace(S1Point)
  177.      * @see #getXAxis()
  178.      * @see #getYAxis()
  179.      */
  180.     public Vector3D getPointAt(final double alpha) {
  181.         final SinCos sc = FastMath.sinCos(alpha);
  182.         return new Vector3D(sc.cos(), x, sc.sin(), y);
  183.     }

  184.     /** Get the X axis of the circle.
  185.      * <p>
  186.      * This method returns the same value as {@link #getPointAt(double)
  187.      * getPointAt(0.0)} but it does not do any computation and always
  188.      * return the same instance.
  189.      * </p>
  190.      * @return an arbitrary x axis on the circle
  191.      * @see #getPointAt(double)
  192.      * @see #getYAxis()
  193.      * @see #getPole()
  194.      */
  195.     public Vector3D getXAxis() {
  196.         return x;
  197.     }

  198.     /** Get the Y axis of the circle.
  199.      * <p>
  200.      * This method returns the same value as {@link #getPointAt(double)
  201.      * getPointAt(MathUtils.SEMI_PI)} but it does not do any computation and always
  202.      * return the same instance.
  203.      * </p>
  204.      * @return an arbitrary y axis point on the circle
  205.      * @see #getPointAt(double)
  206.      * @see #getXAxis()
  207.      * @see #getPole()
  208.      */
  209.     public Vector3D getYAxis() {
  210.         return y;
  211.     }

  212.     /** Get the pole of the circle.
  213.      * <p>
  214.      * As the circle is a great circle, the pole does <em>not</em>
  215.      * belong to it.
  216.      * </p>
  217.      * @return pole of the circle
  218.      * @see #getXAxis()
  219.      * @see #getYAxis()
  220.      */
  221.     public Vector3D getPole() {
  222.         return pole;
  223.     }

  224.     /** Get the arc of the instance that lies inside the other circle.
  225.      * @param other other circle
  226.      * @return arc of the instance that lies inside the other circle
  227.      */
  228.     public Arc getInsideArc(final Circle other) {
  229.         final double alpha  = getPhase(other.pole);
  230.         return new Arc(alpha - MathUtils.SEMI_PI, alpha + MathUtils.SEMI_PI, tolerance);
  231.     }

  232.     /** {@inheritDoc} */
  233.     @Override
  234.     public SubCircle wholeHyperplane() {
  235.         return new SubCircle(this, new ArcsSet(tolerance));
  236.     }

  237.     /** {@inheritDoc} */
  238.     @Override
  239.     public SubCircle emptyHyperplane() {
  240.         final RegionFactory<Sphere1D, S1Point, LimitAngle, SubLimitAngle> factory = new RegionFactory<>();
  241.         return new SubCircle(this, factory.getComplement(new ArcsSet(tolerance)));
  242.     }

  243.     /** Build a region covering the whole space.
  244.      * @return a region containing the instance (really a {@link
  245.      * SphericalPolygonsSet SphericalPolygonsSet} instance)
  246.      */
  247.     @Override
  248.     public SphericalPolygonsSet wholeSpace() {
  249.         return new SphericalPolygonsSet(tolerance);
  250.     }

  251.     /** {@inheritDoc}
  252.      * @see #getOffset(Vector3D)
  253.      */
  254.     @Override
  255.     public double getOffset(final S2Point point) {
  256.         return getOffset(point.getVector());
  257.     }

  258.     /** Get the offset (oriented distance) of a direction.
  259.      * <p>The offset is defined as the angular distance between the
  260.      * circle center and the direction minus the circle radius. It
  261.      * is therefore 0 on the circle, positive for directions outside of
  262.      * the cone delimited by the circle, and negative inside the cone.</p>
  263.      * @param direction direction to check
  264.      * @return offset of the direction
  265.      * @see #getOffset(S2Point)
  266.      */
  267.     public double getOffset(final Vector3D direction) {
  268.         return Vector3D.angle(pole, direction) - MathUtils.SEMI_PI;
  269.     }

  270.     /** {@inheritDoc} */
  271.     @Override
  272.     public S2Point moveToOffset(final S2Point point, final double offset) {
  273.         final SinCos scOld = FastMath.sinCos(getOffset(point));
  274.         final SinCos scNew = FastMath.sinCos(offset);
  275.         final double ratio = scNew.cos() / scOld.cos();
  276.         return new S2Point(new Vector3D(ratio * scOld.sin() - scNew.sin(), pole,
  277.                                         ratio, point.getVector()));
  278.     }

  279.     /** {@inheritDoc} */
  280.     @Override
  281.     public S2Point arbitraryPoint() {
  282.         return new S2Point(pole.orthogonal());
  283.     }

  284.     /** {@inheritDoc} */
  285.     @Override
  286.     public boolean sameOrientationAs(final Circle other) {
  287.         return Vector3D.dotProduct(pole, other.pole) >= 0.0;
  288.     }

  289.     /**
  290.      * Get the arc on this circle between two defining points. Only the point's projection
  291.      * on the circle matters, which is computed using {@link #getPhase(Vector3D)}.
  292.      *
  293.      * @param a first point.
  294.      * @param b second point.
  295.      * @return an arc of the circle.
  296.      */
  297.     public Arc getArc(final S2Point a, final S2Point b) {
  298.         final double phaseA = getPhase(a.getVector());
  299.         double phaseB = getPhase(b.getVector());
  300.         if (phaseB < phaseA) {
  301.             phaseB += 2 * FastMath.PI;
  302.         }
  303.         return new Arc(phaseA, phaseB, tolerance);
  304.     }

  305.     /** Get a {@link org.hipparchus.geometry.partitioning.Transform
  306.      * Transform} embedding a 3D rotation.
  307.      * @param rotation rotation to use
  308.      * @return a new transform that can be applied to either {@link
  309.      * org.hipparchus.geometry.Point Point}, {@link Circle Line} or {@link
  310.      * org.hipparchus.geometry.partitioning.SubHyperplane
  311.      * SubHyperplane} instances
  312.      */
  313.     public static Transform<Sphere2D, S2Point, Circle, SubCircle, Sphere1D, S1Point, LimitAngle, SubLimitAngle>
  314.         getTransform(final Rotation rotation) {
  315.         return new CircleTransform(rotation);
  316.     }

  317.     /** Class embedding a 3D rotation. */
  318.     private static class CircleTransform
  319.         implements Transform<Sphere2D, S2Point, Circle, SubCircle, Sphere1D, S1Point, LimitAngle, SubLimitAngle> {

  320.         /** Underlying rotation. */
  321.         private final Rotation rotation;

  322.         /** Build a transform from a {@code Rotation}.
  323.          * @param rotation rotation to use
  324.          */
  325.         CircleTransform(final Rotation rotation) {
  326.             this.rotation = rotation;
  327.         }

  328.         /** {@inheritDoc} */
  329.         @Override
  330.         public S2Point apply(final S2Point point) {
  331.             return new S2Point(rotation.applyTo(point.getVector()));
  332.         }

  333.         /** {@inheritDoc} */
  334.         @Override
  335.         public Circle apply(final Circle circle) {
  336.             return new Circle(rotation.applyTo(circle.pole),
  337.                               rotation.applyTo(circle.x),
  338.                               rotation.applyTo(circle.y),
  339.                               circle.tolerance);
  340.         }

  341.         /** {@inheritDoc} */
  342.         @Override
  343.         public SubLimitAngle apply(final SubLimitAngle sub, final Circle original, final Circle transformed) {
  344.             // as the circle is rotated, the limit angles are rotated too
  345.             return sub;
  346.         }

  347.     }

  348. }