001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.geometry.spherical.twod; 018 019import org.apache.commons.geometry.core.Transform; 020import org.apache.commons.geometry.euclidean.threed.AffineTransformMatrix3D; 021import org.apache.commons.geometry.euclidean.threed.Vector3D; 022import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation; 023 024/** Implementation of the {@link Transform} interface for spherical 2D points. 025 * 026 * <p>This class uses an {@link AffineTransformMatrix3D} to perform spherical point transforms 027 * in Euclidean 3D space.</p> 028 * 029 * <p>Instances of this class are guaranteed to be immutable.</p> 030 */ 031public final class Transform2S implements Transform<Point2S> { 032 /** Static instance representing the identity transform. */ 033 private static final Transform2S IDENTITY = new Transform2S(AffineTransformMatrix3D.identity()); 034 035 /** Static transform instance that reflects across the x-y plane. */ 036 private static final AffineTransformMatrix3D XY_PLANE_REFLECTION = AffineTransformMatrix3D.createScale(1, 1, -1); 037 038 /** Euclidean transform matrix underlying the spherical transform. */ 039 private final AffineTransformMatrix3D euclideanTransform; 040 041 /** Construct a new instance from its underlying Euclidean transform. 042 * @param euclideanTransform underlying Euclidean transform 043 */ 044 private Transform2S(final AffineTransformMatrix3D euclideanTransform) { 045 this.euclideanTransform = euclideanTransform; 046 } 047 048 /** Get the Euclidean transform matrix underlying the spherical transform. 049 * @return the Euclidean transform matrix underlying the spherical transform 050 */ 051 public AffineTransformMatrix3D getEuclideanTransform() { 052 return euclideanTransform; 053 } 054 055 /** {@inheritDoc} */ 056 @Override 057 public Point2S apply(final Point2S pt) { 058 final Vector3D vec = pt.getVector(); 059 return Point2S.from(euclideanTransform.apply(vec)); 060 } 061 062 /** {@inheritDoc} */ 063 @Override 064 public boolean preservesOrientation() { 065 return euclideanTransform.preservesOrientation(); 066 } 067 068 /** {@inheritDoc} */ 069 @Override 070 public Transform2S inverse() { 071 return new Transform2S(euclideanTransform.inverse()); 072 } 073 074 /** Apply a rotation of {@code angle} radians around the given point to this instance. 075 * @param pt point to rotate around 076 * @param angle rotation angle in radians 077 * @return transform resulting from applying the specified rotation to this instance 078 */ 079 public Transform2S rotate(final Point2S pt, final double angle) { 080 return premultiply(createRotation(pt, angle)); 081 } 082 083 /** Apply a rotation of {@code angle} radians around the given 3D axis to this instance. 084 * @param axis 3D axis of rotation 085 * @param angle rotation angle in radians 086 * @return transform resulting from applying the specified rotation to this instance 087 */ 088 public Transform2S rotate(final Vector3D axis, final double angle) { 089 return premultiply(createRotation(axis, angle)); 090 } 091 092 /** Apply the given quaternion rotation to this instance. 093 * @param quaternion quaternion rotation to apply 094 * @return transform resulting from applying the specified rotation to this instance 095 */ 096 public Transform2S rotate(final QuaternionRotation quaternion) { 097 return premultiply(createRotation(quaternion)); 098 } 099 100 /** Apply a reflection across the equatorial plane defined by the given pole point 101 * to this instance. 102 * @param pole pole point defining the equatorial reflection plane 103 * @return transform resulting from applying the specified reflection to this instance 104 */ 105 public Transform2S reflect(final Point2S pole) { 106 return premultiply(createReflection(pole)); 107 } 108 109 /** Apply a reflection across the equatorial plane defined by the given pole vector 110 * to this instance. 111 * @param poleVector pole vector defining the equatorial reflection plane 112 * @return transform resulting from applying the specified reflection to this instance 113 */ 114 public Transform2S reflect(final Vector3D poleVector) { 115 return premultiply(createReflection(poleVector)); 116 } 117 118 /** Multiply the underlying Euclidean transform of this instance by that of the argument, eg, 119 * {@code other * this}. The returned transform performs the equivalent of 120 * {@code other} followed by {@code this}. 121 * @param other transform to multiply with 122 * @return a new transform computed by multiplying the matrix of this 123 * instance by that of the argument 124 * @see AffineTransformMatrix3D#multiply(AffineTransformMatrix3D) 125 */ 126 public Transform2S multiply(final Transform2S other) { 127 return multiply(this, other); 128 } 129 130 /** Multiply the underlying Euclidean transform matrix of the argument by that of this instance, eg, 131 * {@code this * other}. The returned transform performs the equivalent of {@code this} 132 * followed by {@code other}. 133 * @param other transform to multiply with 134 * @return a new transform computed by multiplying the matrix of the 135 * argument by that of this instance 136 * @see AffineTransformMatrix3D#premultiply(AffineTransformMatrix3D) 137 */ 138 public Transform2S premultiply(final Transform2S other) { 139 return multiply(other, this); 140 } 141 142 /** {@inheritDoc} */ 143 @Override 144 public int hashCode() { 145 return euclideanTransform.hashCode(); 146 } 147 148 /** 149 * Return true if the given object is an instance of {@link Transform2S} 150 * and the underlying Euclidean transform matrices are exactly equal. 151 * @param obj object to test for equality with the current instance 152 * @return true if the underlying transform matrices are exactly equal 153 */ 154 @Override 155 public boolean equals(final Object obj) { 156 if (this == obj) { 157 return true; 158 } 159 if (!(obj instanceof Transform2S)) { 160 return false; 161 } 162 final Transform2S other = (Transform2S) obj; 163 164 return euclideanTransform.equals(other.euclideanTransform); 165 } 166 167 /** {@inheritDoc} */ 168 @Override 169 public String toString() { 170 final StringBuilder sb = new StringBuilder(); 171 172 sb.append(this.getClass().getSimpleName()) 173 .append("[euclideanTransform= ") 174 .append(getEuclideanTransform()) 175 .append(']'); 176 177 return sb.toString(); 178 } 179 180 /** Return an instance representing the identity transform. This transform is guaranteed 181 * to return an <em>equivalent</em> (ie, co-located) point for any input point. However, the 182 * points are not guaranteed to contain exactly equal coordinates. For example, at the poles, an 183 * infinite number of points exist that vary only in the azimuth coordinate. When one of these 184 * points is transformed by this identity transform, the returned point may contain a different 185 * azimuth value from the input, but it will still represent the same location in space. 186 * @return an instance representing the identity transform 187 */ 188 public static Transform2S identity() { 189 return IDENTITY; 190 } 191 192 /** Create a transform that rotates the given angle around {@code pt}. 193 * @param pt point to rotate around 194 * @param angle angle of rotation in radians 195 * @return a transform that rotates the given angle around {@code pt} 196 */ 197 public static Transform2S createRotation(final Point2S pt, final double angle) { 198 return createRotation(pt.getVector(), angle); 199 } 200 201 /** Create a transform that rotates the given angle around {@code axis}. 202 * @param axis 3D axis of rotation 203 * @param angle angle of rotation in radians 204 * @return a transform that rotates the given angle {@code axis} 205 */ 206 public static Transform2S createRotation(final Vector3D axis, final double angle) { 207 return createRotation(QuaternionRotation.fromAxisAngle(axis, angle)); 208 } 209 210 /** Create a transform that performs the given 3D rotation. 211 * @param quaternion quaternion instance representing the 3D rotation 212 * @return a transform that performs the given 3D rotation 213 */ 214 public static Transform2S createRotation(final QuaternionRotation quaternion) { 215 return new Transform2S(quaternion.toMatrix()); 216 } 217 218 /** Create a transform that performs a reflection across the equatorial plane 219 * defined by the given pole point. 220 * @param pole pole point defining the equatorial reflection plane 221 * @return a transform that performs a reflection across the equatorial plane 222 * defined by the given pole point 223 */ 224 public static Transform2S createReflection(final Point2S pole) { 225 return createReflection(pole.getVector()); 226 } 227 228 /** Create a transform that performs a reflection across the equatorial plane 229 * defined by the given pole point. 230 * @param poleVector pole vector defining the equatorial reflection plane 231 * @return a transform that performs a reflection across the equatorial plane 232 * defined by the given pole point 233 */ 234 public static Transform2S createReflection(final Vector3D poleVector) { 235 final QuaternionRotation quat = QuaternionRotation.createVectorRotation(poleVector, Vector3D.Unit.PLUS_Z); 236 237 final AffineTransformMatrix3D matrix = quat.toMatrix() 238 .premultiply(XY_PLANE_REFLECTION) 239 .premultiply(quat.inverse().toMatrix()); 240 241 return new Transform2S(matrix); 242 } 243 244 /** Multiply the Euclidean transform matrices of the arguments together. 245 * @param a first transform 246 * @param b second transform 247 * @return the transform computed as {@code a x b} 248 */ 249 private static Transform2S multiply(final Transform2S a, final Transform2S b) { 250 251 return new Transform2S(a.euclideanTransform.multiply(b.euclideanTransform)); 252 } 253}