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
019import org.apache.commons.geometry.core.partitioning.HyperplaneBoundedRegion;
020import org.apache.commons.numbers.core.Precision;
021
022/** Base class representing an axis-aligned bounding box with minimum and maximum bounding points.
023 * @param <P> Point implementation type
024 * @param <B> Bounds implementation type
025 */
026public abstract class AbstractBounds<
027    P extends EuclideanVector<P>,
028    B extends AbstractBounds<P, B>> {
029
030    /** Minimum point. */
031    private final P min;
032
033    /** Maximum point. */
034    private final P max;
035
036    /** Simple constructor. Callers are responsible for ensuring that all coordinate values are finite and
037     * that all values in {@code min} are less than or equal to their corresponding values in {@code max}.
038     * No validation is performed.
039     * @param min minimum point
040     * @param max maximum point
041     */
042    protected AbstractBounds(final P min, final P max) {
043        this.min = min;
044        this.max = max;
045    }
046
047    /** Get the minimum point.
048     * @return the minimum point
049     */
050    public P getMin() {
051        return min;
052    }
053
054    /** Get the maximum point.
055     * @return the maximum point
056     */
057    public P getMax() {
058        return max;
059    }
060
061    /** Get the diagonal of the bounding box. The return value is a vector pointing from
062     * {@code min} to {@code max} and contains the size of the box along each coordinate axis.
063     * @return the diagonal vector of the bounding box
064     */
065    public P getDiagonal() {
066        return min.vectorTo(max);
067    }
068
069    /** Get the centroid, or geometric center, of the bounding box.
070     * @return the centroid of the bounding box
071     */
072    public P getCentroid() {
073        return min.lerp(max, 0.5);
074    }
075
076    /** Return true if the bounding box has non-zero size along each coordinate axis, as
077     * evaluated by the given precision context.
078     * @param precision precision context used for floating point comparisons
079     * @return true if the bounding box has non-zero size along each coordinate axis
080     */
081    public abstract boolean hasSize(Precision.DoubleEquivalence precision);
082
083    /** Return true if the given point is strictly within or on the boundary of the bounding box.
084     * In other words, true if returned if <code>p<sub>t</sub> &gt;= min<sub>t</sub></code> and
085     * <code>p<sub>t</sub> &lt;= max<sub>t</sub></code> for each coordinate value <code>t</code>.
086     * Floating point comparisons are strict; values are considered equal only if they match exactly.
087     * @param pt the point to check
088     * @return true if the given point is strictly within or on the boundary of the instance
089     * @see #contains(EuclideanVector, Precision.DoubleEquivalence)
090     */
091    public abstract boolean contains(P pt);
092
093    /** Return true if the given point is within or on the boundary of the bounding box, using the given
094     * precision context for floating point comparisons. This is similar to {@link #contains(EuclideanVector)}
095     * but allows points that may be strictly outside of the box due to floating point errors to be considered
096     * inside.
097     * @param pt the point to check
098     * @param precision precision context used to compare floating point values
099     * @return if the given point is within or on the boundary of the bounds, as determined
100     *      by the given precision context
101     * @see #contains(EuclideanVector, Precision.DoubleEquivalence)
102     */
103    public abstract boolean contains(P pt, Precision.DoubleEquivalence precision);
104
105    /** Return true if any point on the interior or boundary of this instance is also considered to be
106     * on the interior or boundary of the argument. Specifically, true is returned if
107     * <code>aMin<sub>t</sub> &lt;= bMax<sub>t</sub></code> and <code>aMax<sub>t</sub> &gt;= bMin<sub>t</sub></code>
108     * for all coordinate values {@code t}, where {@code a} is the current instance and {@code b} is the argument.
109     * Floating point comparisons are strict; values are considered equal only if they match exactly.
110     * @param other bounding box to intersect with
111     * @return true if the bounds intersect
112     */
113    public abstract boolean intersects(B other);
114
115    /** Return the intersection of this bounding box and the argument, or null if no intersection exists.
116     * Floating point comparisons are strict; values are considered equal only if they match exactly. Note
117     * this this method may return bounding boxes with zero size in one or more coordinate axes.
118     * @param other bounding box to intersect with
119     * @return the intersection of this instance and the argument, or null if no such intersection
120     *      exists
121     * @see #intersects(AbstractBounds)
122     */
123    public abstract B intersection(B other);
124
125    /** Return a hyperplane-bounded region containing the same points as this instance.
126     * @param precision precision context used for floating point comparisons in the returned
127     *      region instance
128     * @return a hyperplane-bounded region containing the same points as this instance
129     */
130    public abstract HyperplaneBoundedRegion<P> toRegion(Precision.DoubleEquivalence precision);
131
132    /** Return true if the current instance and argument are considered equal as evaluated by the
133     * given precision context. Bounds are considered equal if they contain equivalent min and max
134     * points.
135     * @param other bounds to compare with
136     * @param precision precision context to compare floating point numbers
137     * @return true if this instance is equivalent to the argument, as evaluated by the given
138     *      precision context
139     * @see EuclideanVector#eq(EuclideanVector, Precision.DoubleEquivalence)
140     */
141    public boolean eq(final B other, final Precision.DoubleEquivalence precision) {
142        return min.eq(other.getMin(), precision) &&
143                max.eq(other.getMax(), precision);
144    }
145
146    /** {@inheritDoc} */
147    @Override
148    public String toString() {
149        final StringBuilder sb = new StringBuilder();
150        sb.append(getClass().getSimpleName())
151            .append("[min= ")
152            .append(min)
153            .append(", max= ")
154            .append(max)
155            .append(']');
156
157        return sb.toString();
158    }
159}