• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *      http://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 package org.apache.commons.math3.geometry.spherical.twod;
18 
19 import org.apache.commons.math3.geometry.Point;
20 import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
21 import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
22 import org.apache.commons.math3.geometry.partitioning.Embedding;
23 import org.apache.commons.math3.geometry.partitioning.Hyperplane;
24 import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
25 import org.apache.commons.math3.geometry.partitioning.Transform;
26 import org.apache.commons.math3.geometry.spherical.oned.Arc;
27 import org.apache.commons.math3.geometry.spherical.oned.ArcsSet;
28 import org.apache.commons.math3.geometry.spherical.oned.S1Point;
29 import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
30 import org.apache.commons.math3.util.FastMath;
31 
32 /** This class represents an oriented great circle on the 2-sphere.
33 
34  * <p>An oriented circle can be defined by a center point. The circle
35  * is the the set of points that are in the normal plan the center.</p>
36 
37  * <p>Since it is oriented the two spherical caps at its two sides are
38  * unambiguously identified as a left cap and a right cap. This can be
39  * used to identify the interior and the exterior in a simple way by
40  * local properties only when part of a line is used to define part of
41  * a spherical polygon boundary.</p>
42 
43  * @since 3.3
44  */
45 public class Circle implements Hyperplane<Sphere2D>, Embedding<Sphere2D, Sphere1D> {
46 
47     /** Pole or circle center. */
48     private Vector3D pole;
49 
50     /** First axis in the equator plane, origin of the phase angles. */
51     private Vector3D x;
52 
53     /** Second axis in the equator plane, in quadrature with respect to x. */
54     private Vector3D y;
55 
56     /** Tolerance below which close sub-arcs are merged together. */
57     private final double tolerance;
58 
59     /** Build a great circle from its pole.
60      * <p>The circle is oriented in the trigonometric direction around pole.</p>
61      * @param pole circle pole
62      * @param tolerance tolerance below which close sub-arcs are merged together
63      */
Circle(final Vector3D pole, final double tolerance)64     public Circle(final Vector3D pole, final double tolerance) {
65         reset(pole);
66         this.tolerance = tolerance;
67     }
68 
69     /** Build a great circle from two non-aligned points.
70      * <p>The circle is oriented from first to second point using the path smaller than \( \pi \).</p>
71      * @param first first point contained in the great circle
72      * @param second second point contained in the great circle
73      * @param tolerance tolerance below which close sub-arcs are merged together
74      */
Circle(final S2Point first, final S2Point second, final double tolerance)75     public Circle(final S2Point first, final S2Point second, final double tolerance) {
76         reset(first.getVector().crossProduct(second.getVector()));
77         this.tolerance = tolerance;
78     }
79 
80     /** Build a circle from its internal components.
81      * <p>The circle is oriented in the trigonometric direction around center.</p>
82      * @param pole circle pole
83      * @param x first axis in the equator plane
84      * @param y second axis in the equator plane
85      * @param tolerance tolerance below which close sub-arcs are merged together
86      */
Circle(final Vector3D pole, final Vector3D x, final Vector3D y, final double tolerance)87     private Circle(final Vector3D pole, final Vector3D x, final Vector3D y,
88                    final double tolerance) {
89         this.pole      = pole;
90         this.x         = x;
91         this.y         = y;
92         this.tolerance = tolerance;
93     }
94 
95     /** Copy constructor.
96      * <p>The created instance is completely independent from the
97      * original instance, it is a deep copy.</p>
98      * @param circle circle to copy
99      */
Circle(final Circle circle)100     public Circle(final Circle circle) {
101         this(circle.pole, circle.x, circle.y, circle.tolerance);
102     }
103 
104     /** {@inheritDoc} */
copySelf()105     public Circle copySelf() {
106         return new Circle(this);
107     }
108 
109     /** Reset the instance as if built from a pole.
110      * <p>The circle is oriented in the trigonometric direction around pole.</p>
111      * @param newPole circle pole
112      */
reset(final Vector3D newPole)113     public void reset(final Vector3D newPole) {
114         this.pole = newPole.normalize();
115         this.x    = newPole.orthogonal();
116         this.y    = Vector3D.crossProduct(newPole, x).normalize();
117     }
118 
119     /** Revert the instance.
120      */
revertSelf()121     public void revertSelf() {
122         // x remains the same
123         y    = y.negate();
124         pole = pole.negate();
125     }
126 
127     /** Get the reverse of the instance.
128      * <p>Get a circle with reversed orientation with respect to the
129      * instance. A new object is built, the instance is untouched.</p>
130      * @return a new circle, with orientation opposite to the instance orientation
131      */
getReverse()132     public Circle getReverse() {
133         return new Circle(pole.negate(), x, y.negate(), tolerance);
134     }
135 
136     /** {@inheritDoc} */
project(Point<Sphere2D> point)137     public Point<Sphere2D> project(Point<Sphere2D> point) {
138         return toSpace(toSubSpace(point));
139     }
140 
141     /** {@inheritDoc} */
getTolerance()142     public double getTolerance() {
143         return tolerance;
144     }
145 
146     /** {@inheritDoc}
147      * @see #getPhase(Vector3D)
148      */
toSubSpace(final Point<Sphere2D> point)149     public S1Point toSubSpace(final Point<Sphere2D> point) {
150         return new S1Point(getPhase(((S2Point) point).getVector()));
151     }
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(Point)
162      */
getPhase(final Vector3D direction)163     public double getPhase(final Vector3D direction) {
164         return FastMath.PI + FastMath.atan2(-direction.dotProduct(y), -direction.dotProduct(x));
165     }
166 
167     /** {@inheritDoc}
168      * @see #getPointAt(double)
169      */
toSpace(final Point<Sphere1D> point)170     public S2Point toSpace(final Point<Sphere1D> point) {
171         return new S2Point(getPointAt(((S1Point) point).getAlpha()));
172     }
173 
174     /** Get a circle point from its phase around the circle.
175      * @param alpha phase around the circle
176      * @return circle point on the sphere
177      * @see #toSpace(Point)
178      * @see #getXAxis()
179      * @see #getYAxis()
180      */
getPointAt(final double alpha)181     public Vector3D getPointAt(final double alpha) {
182         return new Vector3D(FastMath.cos(alpha), x, FastMath.sin(alpha), y);
183     }
184 
185     /** Get the X axis of the circle.
186      * <p>
187      * This method returns the same value as {@link #getPointAt(double)
188      * getPointAt(0.0)} but it does not do any computation and always
189      * return the same instance.
190      * </p>
191      * @return an arbitrary x axis on the circle
192      * @see #getPointAt(double)
193      * @see #getYAxis()
194      * @see #getPole()
195      */
getXAxis()196     public Vector3D getXAxis() {
197         return x;
198     }
199 
200     /** Get the Y axis of the circle.
201      * <p>
202      * This method returns the same value as {@link #getPointAt(double)
203      * getPointAt(0.5 * FastMath.PI)} but it does not do any computation and always
204      * return the same instance.
205      * </p>
206      * @return an arbitrary y axis point on the circle
207      * @see #getPointAt(double)
208      * @see #getXAxis()
209      * @see #getPole()
210      */
getYAxis()211     public Vector3D getYAxis() {
212         return y;
213     }
214 
215     /** Get the pole of the circle.
216      * <p>
217      * As the circle is a great circle, the pole does <em>not</em>
218      * belong to it.
219      * </p>
220      * @return pole of the circle
221      * @see #getXAxis()
222      * @see #getYAxis()
223      */
getPole()224     public Vector3D getPole() {
225         return pole;
226     }
227 
228     /** Get the arc of the instance that lies inside the other circle.
229      * @param other other circle
230      * @return arc of the instance that lies inside the other circle
231      */
getInsideArc(final Circle other)232     public Arc getInsideArc(final Circle other) {
233         final double alpha  = getPhase(other.pole);
234         final double halfPi = 0.5 * FastMath.PI;
235         return new Arc(alpha - halfPi, alpha + halfPi, tolerance);
236     }
237 
238     /** {@inheritDoc} */
wholeHyperplane()239     public SubCircle wholeHyperplane() {
240         return new SubCircle(this, new ArcsSet(tolerance));
241     }
242 
243     /** Build a region covering the whole space.
244      * @return a region containing the instance (really a {@link
245      * SphericalPolygonsSet SphericalPolygonsSet} instance)
246      */
wholeSpace()247     public SphericalPolygonsSet wholeSpace() {
248         return new SphericalPolygonsSet(tolerance);
249     }
250 
251     /** {@inheritDoc}
252      * @see #getOffset(Vector3D)
253      */
getOffset(final Point<Sphere2D> point)254     public double getOffset(final Point<Sphere2D> point) {
255         return getOffset(((S2Point) point).getVector());
256     }
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(Point)
266      */
getOffset(final Vector3D direction)267     public double getOffset(final Vector3D direction) {
268         return Vector3D.angle(pole, direction) - 0.5 * FastMath.PI;
269     }
270 
271     /** {@inheritDoc} */
sameOrientationAs(final Hyperplane<Sphere2D> other)272     public boolean sameOrientationAs(final Hyperplane<Sphere2D> other) {
273         final Circle otherC = (Circle) other;
274         return Vector3D.dotProduct(pole, otherC.pole) >= 0.0;
275     }
276 
277     /** Get a {@link org.apache.commons.math3.geometry.partitioning.Transform
278      * Transform} embedding a 3D rotation.
279      * @param rotation rotation to use
280      * @return a new transform that can be applied to either {@link
281      * Point Point}, {@link Circle Line} or {@link
282      * org.apache.commons.math3.geometry.partitioning.SubHyperplane
283      * SubHyperplane} instances
284      */
getTransform(final Rotation rotation)285     public static Transform<Sphere2D, Sphere1D> getTransform(final Rotation rotation) {
286         return new CircleTransform(rotation);
287     }
288 
289     /** Class embedding a 3D rotation. */
290     private static class CircleTransform implements Transform<Sphere2D, Sphere1D> {
291 
292         /** Underlying rotation. */
293         private final Rotation rotation;
294 
295         /** Build a transform from a {@code Rotation}.
296          * @param rotation rotation to use
297          */
CircleTransform(final Rotation rotation)298         CircleTransform(final Rotation rotation) {
299             this.rotation = rotation;
300         }
301 
302         /** {@inheritDoc} */
apply(final Point<Sphere2D> point)303         public S2Point apply(final Point<Sphere2D> point) {
304             return new S2Point(rotation.applyTo(((S2Point) point).getVector()));
305         }
306 
307         /** {@inheritDoc} */
apply(final Hyperplane<Sphere2D> hyperplane)308         public Circle apply(final Hyperplane<Sphere2D> hyperplane) {
309             final Circle circle = (Circle) hyperplane;
310             return new Circle(rotation.applyTo(circle.pole),
311                               rotation.applyTo(circle.x),
312                               rotation.applyTo(circle.y),
313                               circle.tolerance);
314         }
315 
316         /** {@inheritDoc} */
apply(final SubHyperplane<Sphere1D> sub, final Hyperplane<Sphere2D> original, final Hyperplane<Sphere2D> transformed)317         public SubHyperplane<Sphere1D> apply(final SubHyperplane<Sphere1D> sub,
318                                              final Hyperplane<Sphere2D> original,
319                                              final Hyperplane<Sphere2D> transformed) {
320             // as the circle is rotated, the limit angles are rotated too
321             return sub;
322         }
323 
324     }
325 
326 }
327