/*
* 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.spherical.twod;
import org.apache.commons.math3.geometry.Point;
import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.apache.commons.math3.geometry.partitioning.Embedding;
import org.apache.commons.math3.geometry.partitioning.Hyperplane;
import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
import org.apache.commons.math3.geometry.partitioning.Transform;
import org.apache.commons.math3.geometry.spherical.oned.Arc;
import org.apache.commons.math3.geometry.spherical.oned.ArcsSet;
import org.apache.commons.math3.geometry.spherical.oned.S1Point;
import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
import org.apache.commons.math3.util.FastMath;
This class represents an oriented great circle on the 2-sphere.
An oriented circle can be defined by a center point. The circle
is the the set of points that are in the normal plan the center.
Since it is oriented the two spherical caps at its two sides are
unambiguously identified as a left cap and a right cap. This can be
used to identify the interior and the exterior in a simple way by
local properties only when part of a line is used to define part of
a spherical polygon boundary.
Since: 3.3
/** This class represents an oriented great circle on the 2-sphere.
* <p>An oriented circle can be defined by a center point. The circle
* is the the set of points that are in the normal plan the center.</p>
* <p>Since it is oriented the two spherical caps at its two sides are
* unambiguously identified as a left cap and a right cap. This can be
* used to identify the interior and the exterior in a simple way by
* local properties only when part of a line is used to define part of
* a spherical polygon boundary.</p>
* @since 3.3
*/
public class Circle implements Hyperplane<Sphere2D>, Embedding<Sphere2D, Sphere1D> {
Pole or circle center. /** Pole or circle center. */
private Vector3D pole;
First axis in the equator plane, origin of the phase angles. /** First axis in the equator plane, origin of the phase angles. */
private Vector3D x;
Second axis in the equator plane, in quadrature with respect to x. /** Second axis in the equator plane, in quadrature with respect to x. */
private Vector3D y;
Tolerance below which close sub-arcs are merged together. /** Tolerance below which close sub-arcs are merged together. */
private final double tolerance;
Build a great circle from its pole.
The circle is oriented in the trigonometric direction around pole.
Params: - pole – circle pole
- tolerance – tolerance below which close sub-arcs are merged together
/** Build a great circle from its pole.
* <p>The circle is oriented in the trigonometric direction around pole.</p>
* @param pole circle pole
* @param tolerance tolerance below which close sub-arcs are merged together
*/
public Circle(final Vector3D pole, final double tolerance) {
reset(pole);
this.tolerance = tolerance;
}
Build a great circle from two non-aligned points.
The circle is oriented from first to second point using the path smaller than \( \pi \).
Params: - first – first point contained in the great circle
- second – second point contained in the great circle
- tolerance – tolerance below which close sub-arcs are merged together
/** Build a great circle from two non-aligned points.
* <p>The circle is oriented from first to second point using the path smaller than \( \pi \).</p>
* @param first first point contained in the great circle
* @param second second point contained in the great circle
* @param tolerance tolerance below which close sub-arcs are merged together
*/
public Circle(final S2Point first, final S2Point second, final double tolerance) {
reset(first.getVector().crossProduct(second.getVector()));
this.tolerance = tolerance;
}
Build a circle from its internal components.
The circle is oriented in the trigonometric direction around center.
Params: - pole – circle pole
- x – first axis in the equator plane
- y – second axis in the equator plane
- tolerance – tolerance below which close sub-arcs are merged together
/** Build a circle from its internal components.
* <p>The circle is oriented in the trigonometric direction around center.</p>
* @param pole circle pole
* @param x first axis in the equator plane
* @param y second axis in the equator plane
* @param tolerance tolerance below which close sub-arcs are merged together
*/
private Circle(final Vector3D pole, final Vector3D x, final Vector3D y,
final double tolerance) {
this.pole = pole;
this.x = x;
this.y = y;
this.tolerance = tolerance;
}
Copy constructor.
The created instance is completely independent from the
original instance, it is a deep copy.
Params: - circle – circle to copy
/** Copy constructor.
* <p>The created instance is completely independent from the
* original instance, it is a deep copy.</p>
* @param circle circle to copy
*/
public Circle(final Circle circle) {
this(circle.pole, circle.x, circle.y, circle.tolerance);
}
{@inheritDoc} /** {@inheritDoc} */
public Circle copySelf() {
return new Circle(this);
}
Reset the instance as if built from a pole.
The circle is oriented in the trigonometric direction around pole.
Params: - newPole – circle pole
/** Reset the instance as if built from a pole.
* <p>The circle is oriented in the trigonometric direction around pole.</p>
* @param newPole circle pole
*/
public void reset(final Vector3D newPole) {
this.pole = newPole.normalize();
this.x = newPole.orthogonal();
this.y = Vector3D.crossProduct(newPole, x).normalize();
}
Revert the instance.
/** Revert the instance.
*/
public void revertSelf() {
// x remains the same
y = y.negate();
pole = pole.negate();
}
Get the reverse of the instance.
Get a circle with reversed orientation with respect to the
instance. A new object is built, the instance is untouched.
Returns: a new circle, with orientation opposite to the instance orientation
/** Get the reverse of the instance.
* <p>Get a circle with reversed orientation with respect to the
* instance. A new object is built, the instance is untouched.</p>
* @return a new circle, with orientation opposite to the instance orientation
*/
public Circle getReverse() {
return new Circle(pole.negate(), x, y.negate(), tolerance);
}
{@inheritDoc} /** {@inheritDoc} */
public Point<Sphere2D> project(Point<Sphere2D> point) {
return toSpace(toSubSpace(point));
}
{@inheritDoc} /** {@inheritDoc} */
public double getTolerance() {
return tolerance;
}
{@inheritDoc}
See Also:
/** {@inheritDoc}
* @see #getPhase(Vector3D)
*/
public S1Point toSubSpace(final Point<Sphere2D> point) {
return new S1Point(getPhase(((S2Point) point).getVector()));
}
Get the phase angle of a direction.
The direction may not belong to the circle as the
phase is computed for the meridian plane between the circle
pole and the direction.
Params: - direction – direction for which phase is requested
See Also: Returns: phase angle of the direction around the circle
/** Get the phase angle of a direction.
* <p>
* The direction may not belong to the circle as the
* phase is computed for the meridian plane between the circle
* pole and the direction.
* </p>
* @param direction direction for which phase is requested
* @return phase angle of the direction around the circle
* @see #toSubSpace(Point)
*/
public double getPhase(final Vector3D direction) {
return FastMath.PI + FastMath.atan2(-direction.dotProduct(y), -direction.dotProduct(x));
}
{@inheritDoc}
See Also:
/** {@inheritDoc}
* @see #getPointAt(double)
*/
public S2Point toSpace(final Point<Sphere1D> point) {
return new S2Point(getPointAt(((S1Point) point).getAlpha()));
}
Get a circle point from its phase around the circle.
Params: - alpha – phase around the circle
See Also: Returns: circle point on the sphere
/** Get a circle point from its phase around the circle.
* @param alpha phase around the circle
* @return circle point on the sphere
* @see #toSpace(Point)
* @see #getXAxis()
* @see #getYAxis()
*/
public Vector3D getPointAt(final double alpha) {
return new Vector3D(FastMath.cos(alpha), x, FastMath.sin(alpha), y);
}
Get the X axis of the circle.
This method returns the same value as
getPointAt(0.0)
but it does not do any computation and always return the same instance.
See Also: Returns: an arbitrary x axis on the circle
/** Get the X axis of the circle.
* <p>
* This method returns the same value as {@link #getPointAt(double)
* getPointAt(0.0)} but it does not do any computation and always
* return the same instance.
* </p>
* @return an arbitrary x axis on the circle
* @see #getPointAt(double)
* @see #getYAxis()
* @see #getPole()
*/
public Vector3D getXAxis() {
return x;
}
Get the Y axis of the circle.
This method returns the same value as
getPointAt(0.5 * FastMath.PI)
but it does not do any computation and always return the same instance.
See Also: Returns: an arbitrary y axis point on the circle
/** Get the Y axis of the circle.
* <p>
* This method returns the same value as {@link #getPointAt(double)
* getPointAt(0.5 * FastMath.PI)} but it does not do any computation and always
* return the same instance.
* </p>
* @return an arbitrary y axis point on the circle
* @see #getPointAt(double)
* @see #getXAxis()
* @see #getPole()
*/
public Vector3D getYAxis() {
return y;
}
Get the pole of the circle.
As the circle is a great circle, the pole does not
belong to it.
See Also: Returns: pole of the circle
/** Get the pole of the circle.
* <p>
* As the circle is a great circle, the pole does <em>not</em>
* belong to it.
* </p>
* @return pole of the circle
* @see #getXAxis()
* @see #getYAxis()
*/
public Vector3D getPole() {
return pole;
}
Get the arc of the instance that lies inside the other circle.
Params: - other – other circle
Returns: arc of the instance that lies inside the other circle
/** Get the arc of the instance that lies inside the other circle.
* @param other other circle
* @return arc of the instance that lies inside the other circle
*/
public Arc getInsideArc(final Circle other) {
final double alpha = getPhase(other.pole);
final double halfPi = 0.5 * FastMath.PI;
return new Arc(alpha - halfPi, alpha + halfPi, tolerance);
}
{@inheritDoc} /** {@inheritDoc} */
public SubCircle wholeHyperplane() {
return new SubCircle(this, new ArcsSet(tolerance));
}
Build a region covering the whole space.
Returns: a region containing the instance (really a SphericalPolygonsSet
instance)
/** Build a region covering the whole space.
* @return a region containing the instance (really a {@link
* SphericalPolygonsSet SphericalPolygonsSet} instance)
*/
public SphericalPolygonsSet wholeSpace() {
return new SphericalPolygonsSet(tolerance);
}
{@inheritDoc}
See Also:
/** {@inheritDoc}
* @see #getOffset(Vector3D)
*/
public double getOffset(final Point<Sphere2D> point) {
return getOffset(((S2Point) point).getVector());
}
Get the offset (oriented distance) of a direction.
The offset is defined as the angular distance between the
circle center and the direction minus the circle radius. It
is therefore 0 on the circle, positive for directions outside of
the cone delimited by the circle, and negative inside the cone.
Params: - direction – direction to check
See Also: Returns: offset of the direction
/** Get the offset (oriented distance) of a direction.
* <p>The offset is defined as the angular distance between the
* circle center and the direction minus the circle radius. It
* is therefore 0 on the circle, positive for directions outside of
* the cone delimited by the circle, and negative inside the cone.</p>
* @param direction direction to check
* @return offset of the direction
* @see #getOffset(Point)
*/
public double getOffset(final Vector3D direction) {
return Vector3D.angle(pole, direction) - 0.5 * FastMath.PI;
}
{@inheritDoc} /** {@inheritDoc} */
public boolean sameOrientationAs(final Hyperplane<Sphere2D> other) {
final Circle otherC = (Circle) other;
return Vector3D.dotProduct(pole, otherC.pole) >= 0.0;
}
Get a
Transform
embedding a 3D rotation. Params: - rotation – rotation to use
Returns: a new transform that can be applied to either Point
, Line
or
SubHyperplane
instances
/** Get a {@link org.apache.commons.math3.geometry.partitioning.Transform
* Transform} embedding a 3D rotation.
* @param rotation rotation to use
* @return a new transform that can be applied to either {@link
* Point Point}, {@link Circle Line} or {@link
* org.apache.commons.math3.geometry.partitioning.SubHyperplane
* SubHyperplane} instances
*/
public static Transform<Sphere2D, Sphere1D> getTransform(final Rotation rotation) {
return new CircleTransform(rotation);
}
Class embedding a 3D rotation. /** Class embedding a 3D rotation. */
private static class CircleTransform implements Transform<Sphere2D, Sphere1D> {
Underlying rotation. /** Underlying rotation. */
private final Rotation rotation;
Build a transform from a Rotation
. Params: - rotation – rotation to use
/** Build a transform from a {@code Rotation}.
* @param rotation rotation to use
*/
CircleTransform(final Rotation rotation) {
this.rotation = rotation;
}
{@inheritDoc} /** {@inheritDoc} */
public S2Point apply(final Point<Sphere2D> point) {
return new S2Point(rotation.applyTo(((S2Point) point).getVector()));
}
{@inheritDoc} /** {@inheritDoc} */
public Circle apply(final Hyperplane<Sphere2D> hyperplane) {
final Circle circle = (Circle) hyperplane;
return new Circle(rotation.applyTo(circle.pole),
rotation.applyTo(circle.x),
rotation.applyTo(circle.y),
circle.tolerance);
}
{@inheritDoc} /** {@inheritDoc} */
public SubHyperplane<Sphere1D> apply(final SubHyperplane<Sphere1D> sub,
final Hyperplane<Sphere2D> original,
final Hyperplane<Sphere2D> transformed) {
// as the circle is rotated, the limit angles are rotated too
return sub;
}
}
}