blob: 5d2449d842e27354533d9879e39624a75d81ea1d [file] [log] [blame]
/*
* 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.geometry.spherical.oned;
import org.apache.commons.geometry.core.Transform;
/** Implementation of the {@link Transform} interface for spherical 1D points.
*
* <p>Similar to the Euclidean 1D
* {@link org.apache.commons.geometry.euclidean.oned.AffineTransformMatrix1D AffineTransformMatrix1D},
* this class performs transformations using an internal 1D affine transformation matrix. In the
* Euclidean case, the matrix contains a scale factor and a translation. Here, the matrix contains
* a scale/negation factor that takes the values -1 or +1, and a rotation value. This restriction on
* the allowed values in the matrix is required in order to fulfill the geometric requirements
* of the {@link Transform} interface. For example, if arbitrary scaling is allowed, the point {@code 0.5pi}
* could be scaled by 4 to {@code 2pi}, which is equivalent to {@code 0pi}. However, if the inverse scaling
* of {@code 1/4} is applied to {@code 0pi}, the result is {@code 0pi} and not {@code 0.5pi}. This breaks
* the {@link Transform} requirement that transforms be inversible.
* </p>
*
* <p>Instances of this class are guaranteed to be immutable.</p>
*/
public final class Transform1S implements Transform<Point1S> {
/** Static instance representing the identity transform. */
private static final Transform1S IDENTITY = new Transform1S(1, 0);
/** Static instance that negates azimuth values. */
private static final Transform1S NEGATION = new Transform1S(-1, 0);
/** Value to scale the point azimuth by. This will only be +1/-1. */
private final double scale;
/** Value to rotate the point azimuth by. */
private final double rotate;
/** Construct a new instance from its transform components.
* @param scale scale value for the transform; must only be +1 or -1
* @param rotate rotation value
*/
private Transform1S(final double scale, final double rotate) {
this.scale = scale;
this.rotate = rotate;
}
/** Return true if the transform negates the azimuth values of transformed
* points, regardless of any rotation applied subsequently.
* @return true if the transform negates the azimuth values of transformed
* points
* @see #preservesOrientation()
*/
public boolean isNegation() {
return scale <= 0;
}
/** Get the rotation value applied by this instance, in radians.
* @return the rotation value applied by this instance, in radians.
*/
public double getRotation() {
return rotate;
}
/** {@inheritDoc} */
@Override
public Point1S apply(final Point1S pt) {
final double az = pt.getAzimuth();
final double resultAz = (az * scale) + rotate;
return Point1S.of(resultAz);
}
/** {@inheritDoc} */
@Override
public boolean preservesOrientation() {
return !isNegation();
}
/** Return a new transform created by pre-multiplying this instance by a transform
* producing a rotation with the given angle.
* @param angle angle to rotate, in radians
* @return a new transform created by pre-multiplying this instance by a transform
* producing a rotation with the given angle
* @see #createRotation(double)
*/
public Transform1S rotate(final double angle) {
return premultiply(createRotation(angle));
}
/** Return a new transform created by pre-multiplying this instance by a transform
* that negates azimuth values.
* @return a new transform created by pre-multiplying this instance by a transform
* that negates azimuth values
*/
public Transform1S negate() {
return premultiply(createNegation());
}
/** Multiply the underlying matrix of this instance by that of the argument, eg,
* {@code other * this}. The returned transform performs the equivalent of
* {@code other} followed by {@code this}.
* @param other transform to multiply with
* @return a new transform computed by multiplying the matrix of this
* instance by that of the argument
*/
public Transform1S multiply(final Transform1S other) {
return multiply(this, other);
}
/** Multiply the underlying matrix of the argument by that of this instance, eg,
* {@code this * other}. The returned transform performs the equivalent of {@code this}
* followed by {@code other}.
* @param other transform to multiply with
* @return a new transform computed by multiplying the matrix of the
* argument by that of this instance
*/
public Transform1S premultiply(final Transform1S other) {
return multiply(other, this);
}
/** Return a transform that is the inverse of the current instance. The returned transform
* will undo changes applied by this instance.
* @return a transform that is the inverse of the current instance
*/
public Transform1S inverse() {
final double invScale = 1.0 / scale;
final double resultScale = invScale;
final double resultRotate = -(rotate * invScale);
return new Transform1S(resultScale, resultRotate);
}
/** {@inheritDoc} */
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (result * prime) + Double.hashCode(scale);
result = (result * prime) + Double.hashCode(rotate);
return result;
}
/**
* Return true if the given object is an instance of {@link Transform1S}
* and all transform element values are exactly equal.
* @param obj object to test for equality with the current instance
* @return true if all transform elements are exactly equal; otherwise false
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Transform1S)) {
return false;
}
final Transform1S other = (Transform1S) obj;
return Double.compare(scale, other.scale) == 0 &&
Double.compare(rotate, other.rotate) == 0;
}
/** {@inheritDoc} */
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getSimpleName())
.append("[negate= ")
.append(isNegation())
.append(", rotate= ")
.append(getRotation())
.append("]");
return sb.toString();
}
/** Return a transform instance representing the identity transform.
* @return a transform instance representing the identity transform
*/
public static Transform1S identity() {
return IDENTITY;
}
/** Return a transform instance that negates azimuth values.
* @return a transform instance that negates azimuth values.
*/
public static Transform1S createNegation() {
return NEGATION;
}
/** Return a transform instance that performs a rotation with the given
* angle.
* @param angle angle of the rotation, in radians
* @return a transform instance that performs a rotation with the given
* angle
*/
public static Transform1S createRotation(final double angle) {
return new Transform1S(1, angle);
}
/** Multiply two transforms together as matrices.
* @param a first transform
* @param b second transform
* @return the transform computed as {@code a x b}
*/
private static Transform1S multiply(final Transform1S a, final Transform1S b) {
// calculate the matrix elements
final double resultScale = a.scale * b.scale;
final double resultRotate = (a.scale * b.rotate) + a.rotate;
return new Transform1S(resultScale, resultRotate);
}
}