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.euclidean;
018
019/** Base class for affine transform matrices in Euclidean space.
020 *
021 * @param <V> Vector/point implementation type defining the space.
022 * @param <M> Matrix transform implementation type.
023 */
024public abstract class AbstractAffineTransformMatrix<
025        V extends EuclideanVector<V>,
026        M extends AbstractAffineTransformMatrix<V, M>>
027    implements EuclideanTransform<V> {
028
029    /** Apply this transform to the given vector, ignoring translations and normalizing the
030     * result. This is equivalent to {@code transform.applyVector(vec).normalize()} but without
031     * the intermediate vector instance.
032     *
033     * @param vec the vector to transform
034     * @return the new, transformed unit vector
035     * @throws IllegalArgumentException if the transformed vector coordinates cannot be normalized
036     * @see #applyVector(EuclideanVector)
037     */
038    public abstract V applyDirection(V vec);
039
040    /** Get the determinant of the matrix.
041     * @return the determinant of the matrix
042     */
043    public abstract double determinant();
044
045    /** {@inheritDoc}
046     * @throws IllegalStateException if the matrix cannot be inverted
047     */
048    @Override
049    public abstract M inverse();
050
051    /** Return a matrix containing only the linear portion of this transform.
052     * The returned instance contains the same matrix elements as this instance
053     * but with the translation component set to zero.
054     * @return a matrix containing only the linear portion of this transform
055     */
056    public abstract M linear();
057
058    /** Return a matrix containing the transpose of the linear portion of this transform.
059     * The returned instance is linear, meaning it has a translation component of zero.
060     * @return a matrix containing the transpose of the linear portion of this transform
061     */
062    public abstract M linearTranspose();
063
064    /** Return a transform suitable for transforming normals. The returned matrix is
065     * the inverse transpose of the linear portion of this instance, i.e.
066     * <code>N = (L<sup>-1</sup>)<sup>T</sup></code>, where <code>L</code> is the linear portion
067     * of this instance and <code>N</code> is the returned matrix. Note that normals
068     * transformed with the returned matrix may be scaled during transformation and require
069     * normalization.
070     * @return a transform suitable for transforming normals
071     * @throws IllegalStateException if the matrix cannot be inverted
072     * @see <a href="https://en.wikipedia.org/wiki/Normal_(geometry)#Transforming_normals">Transforming normals</a>
073     */
074    public M normalTransform() {
075        return inverse().linearTranspose();
076    }
077
078    /** {@inheritDoc}
079     *
080     * <p>This method returns true if the determinant of the matrix is positive.</p>
081     */
082    @Override
083    public boolean preservesOrientation() {
084        return determinant() > 0.0;
085    }
086}