BaseConic.java

/*
 * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
 *
 * Licensed 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 com.irurueta.geometry;

import com.irurueta.algebra.Matrix;
import com.irurueta.algebra.Utils;
import com.irurueta.algebra.WrongSizeException;

import java.io.Serializable;

/**
 * Class defining the base interface of any possible conic.
 * Appropriate subclasses should be used for each conic type: pure conics and
 * dual conics.
 */
public abstract class BaseConic implements Serializable {
    /**
     * Number of rows of one matrix that contains conic parameters.
     */
    public static final int BASECONIC_MATRIX_ROW_SIZE = 3;

    /**
     * Number of columns of one matrix that contains conic parameters.
     */
    public static final int BASECONIC_MATRIX_COLUMN_SIZE = 3;

    /**
     * Number of parameters on a conic or dual conic.
     */
    public static final int N_PARAMS = 6;

    /**
     * Threshold above zero used to determine whether a point lies inside
     * (is locus of) the given conic or not.
     */
    public static final double DEFAULT_LOCUS_THRESHOLD = 1e-12;

    /**
     * Threshold above zero used to determine whether two points of a conic
     * are perpendicular or not.
     */
    public static final double DEFAULT_PERPENDICULAR_THRESHOLD = 1e-12;

    /**
     * Minimum allowed threshold.
     */
    public static final double MIN_THRESHOLD = 0.0;

    /**
     * Threshold above zero to determine whether one matrix is symmetric or not.
     */
    private static final double DEFAULT_SYMMETRIC_THRESHOLD = 1e-12;

    /**
     * Machine precision.
     */
    private static final double PRECISION = 1e-12;

    /**
     * "A" element of the matrix defining a conic.
     */
    protected double a;

    /**
     * B element of the matrix defining a conic.
     */
    protected double b;

    /**
     * C element of the matrix defining a conic.
     */
    protected double c;

    /**
     * D element of the matrix defining a conic.
     */
    private double d;

    /**
     * E element of the matrix defining a conic.
     */
    private double e;

    /**
     * F element of the matrix defining a conic.
     */
    private double f;

    /**
     * Determines whether this instance is already mNormalized.
     */
    private boolean normalized;

    /**
     * Constructor of this class.
     */
    protected BaseConic() {
        a = b = c = d = e = f = 0.0;
        normalized = false;
    }

    /**
     * Constructor of this class. This constructor accepts every parameter
     * describing a base conic (parameters a, b, c, d, e, f).
     *
     * @param a Parameter A of the base conic
     * @param b Parameter B of the base conic.
     * @param c Parameter C of the base conic.
     * @param d Parameter D of the base conic.
     * @param e Parameter E of the base conic.
     * @param f Parameter F of the base conic.
     */
    protected BaseConic(final double a, final double b, final double c, final double d, final double e,
                        final double f) {
        setParameters(a, b, c, d, e, f);
    }

    /**
     * Constructor. This constructor accepts a Matrix describing a base conic.
     *
     * @param m 3x3 matrix describing a base conic.
     * @throws NonSymmetricMatrixException Raised when the conic matrix is not
     *                                     symmetric.
     * @throws IllegalArgumentException    Raised when the size of the matrix is
     *                                     not 3x3.
     */
    protected BaseConic(final Matrix m) throws NonSymmetricMatrixException {
        setParameters(m);
    }

    /**
     * Returns parameter A of the given base conic.
     *
     * @return Parameter A of a matrix describing a base conic.
     */
    public double getA() {
        return a;
    }

    /**
     * Returns parameter B of the given base conic.
     *
     * @return Parameter B of a matrix describing a base conic.
     */
    public double getB() {
        return b;
    }

    /**
     * Returns parameter C of the given base conic.
     *
     * @return Parameter C of a matrix describing a base conic.
     */
    public double getC() {
        return c;
    }

    /**
     * Returns parameter D of the given base conic.
     *
     * @return Parameter D of a matrix describing a base conic.
     */
    public double getD() {
        return d;
    }

    /**
     * Returns parameter E of the given base conic.
     *
     * @return Parameter E of a matrix describing a base conic.
     */
    public double getE() {
        return e;
    }

    /**
     * Returns parameter F of the given base conic.
     *
     * @return Parameter F of a matrix describing a base conic.
     */
    public double getF() {
        return f;
    }

    /**
     * This method accepts every parameter describing a base conic (parameters
     * a, b, c, d, e, f).
     *
     * @param a Parameter A of the base conic.
     * @param b Parameter B of the base conic.
     * @param c Parameter C of the base conic.
     * @param d Parameter D of the base conic.
     * @param e Parameter E of the base conic.
     * @param f Parameter F of the base conic.
     */
    public final void setParameters(final double a, final double b, final double c, final double d, final double e,
                                    final double f) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
        this.e = e;
        this.f = f;
        normalized = false;
    }

    /**
     * This method sets the matrix used for describing a base conic.
     * This matrix must be 3x3 and symmetric.
     *
     * @param m                  3x3 Matrix describing a base conic.
     * @param symmetricThreshold Grade of tolerance to determine whether a
     *                           matrix is symmetric or not. Because of machine precision,
     *                           the values may not be exactly equal. (by default:
     *                           DEFAULT_SYMMETRIC_THRESHOLD is used).
     * @throws IllegalArgumentException    Raised when the size of the matrix is
     *                                     not 3x3.
     * @throws NonSymmetricMatrixException Raised when the conic matrix is not
     *                                     symmetric.
     */
    @SuppressWarnings("DuplicatedCode")
    public final void setParameters(final Matrix m, final double symmetricThreshold)
            throws NonSymmetricMatrixException {
        if (m.getRows() != BASECONIC_MATRIX_ROW_SIZE || m.getColumns() != BASECONIC_MATRIX_COLUMN_SIZE) {
            throw new IllegalArgumentException();
        } else {
            if (!Utils.isSymmetric(m, symmetricThreshold)) {
                throw new NonSymmetricMatrixException();
            } else {
                a = m.getElementAt(0, 0);
                b = m.getElementAt(0, 1);
                c = m.getElementAt(1, 1);
                d = m.getElementAt(0, 2);
                e = m.getElementAt(1, 2);
                f = m.getElementAt(2, 2);
                normalized = false;
            }
        }
    }

    /**
     * This method sets the matrix used for describing a base conic.
     * This matrix must be 3x3 and symmetric.
     *
     * @param m 3x3 Matrix describing a base conic.
     * @throws IllegalArgumentException    Raised when the size of the matrix is
     *                                     not 3x3.
     * @throws NonSymmetricMatrixException Raised when the conic matrix is not
     *                                     symmetric.
     */
    public final void setParameters(final Matrix m) throws NonSymmetricMatrixException {
        setParameters(m, DEFAULT_SYMMETRIC_THRESHOLD);
    }

    /**
     * This method sets the A parameter of a base conic.
     *
     * @param a Parameter A of the given base conic.
     */
    public void setA(final double a) {
        this.a = a;
        normalized = false;
    }

    /**
     * This method sets the B parameter of a base conic.
     *
     * @param b Parameter B of the given base conic.
     */
    public void setB(final double b) {
        this.b = b;
        normalized = false;
    }

    /**
     * This method sets the C parameter of a base conic.
     *
     * @param c Parameter C of the given base conic.
     */
    public void setC(final double c) {
        this.c = c;
        normalized = false;
    }

    /**
     * This method sets the D parameter of a base conic.
     *
     * @param d Parameter D of the given base conic.
     */
    public void setD(final double d) {
        this.d = d;
        normalized = false;
    }

    /**
     * This method sets the E parameter of a base conic.
     *
     * @param e Parameter E of the given base conic.
     */
    public void setE(final double e) {
        this.e = e;
        normalized = false;
    }

    /**
     * This method sets the F parameter of a base conic.
     *
     * @param f Parameter F of the given base conic.
     */
    public void setF(final double f) {
        this.f = f;
        normalized = false;
    }

    /**
     * Returns the matrix that describes this base conic.
     *
     * @return 3x3 matrix describing this base conic.
     */
    public Matrix asMatrix() {
        Matrix out = null;
        try {
            out = new Matrix(BASECONIC_MATRIX_ROW_SIZE, BASECONIC_MATRIX_COLUMN_SIZE);
            asMatrix(out);
        } catch (final WrongSizeException ignore) {
            // never happens
        }
        return out;
    }

    /**
     * Sets the values in provided matrix corresponding to this base conic.
     *
     * @param m Provided matrix where values will be stored
     * @throws IllegalArgumentException Raised if provided matrix is not 3x3
     */
    public void asMatrix(final Matrix m) {

        if (m.getRows() != BASECONIC_MATRIX_ROW_SIZE || m.getColumns() != BASECONIC_MATRIX_ROW_SIZE) {
            throw new IllegalArgumentException();
        }

        m.setElementAt(0, 0, a);
        m.setElementAt(0, 1, b);
        m.setElementAt(0, 2, d);
        m.setElementAt(1, 0, b);
        m.setElementAt(1, 1, c);
        m.setElementAt(1, 2, e);
        m.setElementAt(2, 0, d);
        m.setElementAt(2, 1, e);
        m.setElementAt(2, 2, f);
    }

    /**
     * Normalizes the Conic params using its norm
     */
    public void normalize() {
        if (!normalized) {
            final var m = asMatrix();
            final var norm = Utils.normF(m);

            if (!Double.isNaN(norm) && norm > PRECISION) {
                a /= norm;
                b /= norm;
                c /= norm;
                d /= norm;
                e /= norm;
                f /= norm;
                normalized = true;
            }
        }
    }

    /**
     * Returns boolean indicating whether this base conic has already been
     * normalized.
     *
     * @return True if normalized, false otherwise.
     */
    public boolean isNormalized() {
        return normalized;
    }
}