All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.leeds.ccg.v3d.geometry.V3D_LineSegment Maven / Gradle / Ivy

There is a newer version: 0.8
Show newest version
/*
 * Copyright 2019 Andy Turner, University of Leeds.
 *
 * 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 uk.ac.leeds.ccg.v3d.geometry;

import ch.obermuhlner.math.big.BigRational;
import java.math.BigDecimal;
import java.util.Objects;
import uk.ac.leeds.ccg.math.Math_BigRationalSqrt;

/**
 * 3D representation of a finite length line (a line segment). The line begins
 * at the point {@link #p}, travels in the direction {@link #v} and ends at the
 * point {@link #q}. The "*" denotes a point in 3D and the line is shown with a
 * line of "e" symbols in the following depiction: {@code
 *                                       z
 *                          y           +
 *                          +          /                * p=
 *                          |         /                e
 *                          |        /                e
 *                          |    z0-/                e
 *                          |      /                e
 *                          |     /               e
 *                          |    /               e
 *                          |   /               e
 *                       y0-|  /               e
 *                          | /               e
 *                          |/         x1    e
 *  - ----------------------|-----------/---e---/---- + x
 *                         /|              e   x0
 *                        / |-y1          e
 *                       /  |           e
 *                      /   |          e
 *                  z1-/    |         e
 *                    /     |        e
 *                   /      |       * q=
 *                  /       |
 *                 /        |
 *                -         |
 *                          -
 * }
 *
 * @author Andy Turner
 * @version 1.0
 */
public class V3D_LineSegment extends V3D_Line implements V3D_FiniteGeometry {

    private static final long serialVersionUID = 1L;

    /**
     * For storing the envelope of this.
     */
    protected V3D_Envelope en;

    /**
     * Stores the length of the line squared.
     */
    protected final BigRational len2;

    /**
     * Create a new instance.
     * 
     * @param l What {@code this} is created from.
     */
    public V3D_LineSegment(V3D_LineSegment l) {
        super(l);
        len2 = p.getDistanceSquared(q);
    }

    /**
     * Create a new instance.
     * 
     * @param p What {@link #p} is set to.
     * @param q What {@link #q} is set to.
     */
    public V3D_LineSegment(V3D_Point p, V3D_Point q) {
        super(p, q);
        len2 = p.getDistanceSquared(q);
    }

    /**
     * Create a new instance.
     * 
     * @param l What {@code this} is created from.
     */
    public V3D_LineSegment(V3D_Line l) {
        super(l);
        len2 = p.getDistanceSquared(q);
    }

    /**
     * Create a new instance.
     * 
     * @param l What {@code this} is created from.
     */
    public V3D_LineSegment(V3D_Envelope.LineSegment l) {
        this(new V3D_Point(l.p), new V3D_Point(l.q));
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof V3D_LineSegment) {
            V3D_LineSegment l = (V3D_LineSegment) o;
            if (l.p.equals(p) && l.q.equals(q)) {
                return true;
            }
            if (l.p.equals(q) && l.q.equals(p)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param l The line segment to test if it is the same as {@code this}.
     * @return {@code true} iff {@code l} is the same as {@code this}.
     */
    public boolean equals(V3D_LineSegment l) {
        return p.equals(l.p) && q.equals(l.q);
    }

    /**
     * @param l The line segment to test if it is equal to {@code this}.
     * @return {@code true} iff {@code this} is equal to {@code l} ignoring the
     * direction of {@link #v}.
     */
    public boolean equalsIgnoreDirection(V3D_LineSegment l) {
        if (equals(l)) {
            return true;
        } else {
            return p.equals(l.q) && q.equals(l.p);
        }
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 31 * hash + Objects.hashCode(this.p);
        hash = 31 * hash + Objects.hashCode(this.q);
        return hash;
    }

    /**
     * @return {@code true} iff this line segment is effectively a point.
     */
    public boolean isPoint() {
        return p.equals(q);
    }

    /**
     * @param v The vector to apply to each coordinate of this.
     * @return a new V3D_LineSegment which is {@code this} with the {@code v}
     * applied.
     */
    @Override
    public V3D_LineSegment apply(V3D_Vector v) {
        return new V3D_LineSegment(p.apply(v), q.apply(v));
    }

    /**
     * @param oom The Order of Magnitude for the precision of the result.
     * @return The length of this as a BigDecimal
     */
    public BigDecimal getLength(int oom) {
        return p.getDistance(q, oom);
    }

    /**
     * @return The length of this squared as a BigRational.
     */
    public BigRational getLength2() {
        return p.getDistanceSquared(q);
    }

    /**
     * @return {@code new V3D_Envelope(start, end)}
     */
    @Override
    public V3D_Envelope getEnvelope() {
        if (en == null) {
            en = new V3D_Envelope(p, q);
        }
        return en;
    }

    /**
     * @param p A point to test for intersection within the specified tolerance.
     * @return true if p is within t of this given scale.
     */
    @Override
    public boolean isIntersectedBy(V3D_Point p) {
        boolean ei = getEnvelope().isIntersectedBy(p.getEnvelope());
        if (ei) {
            if (super.isIntersectedBy(p)) {
                Math_BigRationalSqrt a = p.getDistance(this.p);
                if (a.getX().isZero()) {
                    return true;
                }
                Math_BigRationalSqrt b = p.getDistance(this.q);
                if (b.getX().isZero()) {
                    return true;
                }
                Math_BigRationalSqrt l = this.p.getDistance(this.q);
                if (a.add(b).compareTo(l) != 1) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * @param l A line segment to indicate intersection with this.
     * @param flag Used to distinguish this method from
     * {@link #isIntersectedBy(uk.ac.leeds.ccg.v3d.geometry.V3D_Line)}. The
     * value is ignored.
     * @return {@code true} iff {@code l} intersects with {@code this}.
     */
    @Override
    public boolean isIntersectedBy(V3D_LineSegment l, boolean flag) {
        boolean ei = getEnvelope().isIntersectedBy(l.getEnvelope());
        if (ei) {
            return super.isIntersectedBy(l);
        }
        return false;
    }

    /**
     * @param l A line to test for intersection within the specified tolerance.
     * @return true if p is within t of this given scale.
     */
    @Override
    public boolean isIntersectedBy(V3D_Line l) {
        V3D_Geometry i = super.getIntersection(l);
        if (i == null) {
            return false;
        }
        if (i instanceof V3D_Point) {
            return isIntersectedBy((V3D_Point) i);
        } else {
            return true;
        }
    }

    /**
     * Intersects {@code this} with {@code l}. If they are equivalent then
     * return {@code this}. If they overlap in a line return the part that
     * overlaps (the order of points is not defined). If they intersect at a
     * point, the point is returned. {@code null} is returned if the two line
     * segments do not intersect.
     *
     * @param l The line to get intersection with this.
     * @return The intersection between {@code this} and {@code l}.
     */
    @Override
    public V3D_Geometry getIntersection(V3D_Line l) {
        // Check if infinite lines intersect.
        V3D_Geometry i = V3D_Line.getIntersection(new V3D_Line(this),
                new V3D_Line(l));
        if (i == null) {
            // There is no intersection.
            return i;
        }
        /**
         * If infinite lines intersects at a point, then check this point is on
         * this.
         */
        if (i instanceof V3D_Point) {
            if (isIntersectedBy((V3D_Point) i)) {
                return i;
            } else {
                return null;
            }
        } else if (i instanceof V3D_Line) {
            // If the lines are the same, then return this. 
            return this;
        } else {
            // There is no intersection.
            return null;
        }
    }

    /**
     * Intersects {@code this} with {@code l}. If they are equivalent then
     * return {@code this}. If they overlap in a line return the part that
     * overlaps (the order of points is not defined). If they intersect at a
     * point, the point is returned. {@code null} is returned if the two line
     * segments do not intersect.
     *
     * @param l The line to get intersection with this.
     * @param flag To distinguish this method from
     * {@link #getIntersection(uk.ac.leeds.ccg.v3d.geometry.V3D_Line)}. The
     * value is ignored.
     * @return The intersection between {@code this} and {@code l}.
     */
    @Override
    public V3D_Geometry getIntersection(V3D_LineSegment l, boolean flag) {
        return getIntersection(this, l);
    }

    /**
     * A utility method for calculating and returning the intersection between
     * {@code l0} and {@code l1}
     *
     * @param l0 Line to intersect with {@code l1}.
     * @param l1 Line to intersect with {@code l0}.
     * @return The intersection between {@code l0} and {@code l1}.
     */
    public static V3D_Geometry getIntersection(V3D_LineSegment l0,
            V3D_LineSegment l1) {
        V3D_Envelope ren = l0.getEnvelope().getIntersection(l1.getEnvelope());
        if (ren == null) {
            return null;
        }
        // Get intersection of infinite lines. 
        V3D_Geometry li = V3D_Line.getIntersection(new V3D_Line(l0),
                new V3D_Line(l1));
        if (li == null) {
            return null;
        }
        if (li instanceof V3D_Point) {
            return li;
        }
        /**
         * Check the type of intersection. {@code
         * 1)   p ---------- q
         *         l.p ---------- l.q
         * 2)   p ------------------------ q
         *         l.p ---------- l.q
         * 3)        p ---------- q
         *    l.p ------------------------ l.q
         * 4)        p ---------- q
         *    l.p ---------- l.q
         * 5)   q ---------- p
         *         l.p ---------- l.q
         * 6)   q ------------------------ p
         *         l.p ---------- l.q
         * 7)        q ---------- p
         *    l.p ------------------------ l.q
         * 8)        q ---------- p
         *    l.p ---------- l.q
         * 9)   p ---------- q
         *         l.q ---------- l.p
         * 10)   p ------------------------ q
         *         l.q ---------- l.p
         * 11)       p ---------- q
         *    l.q ------------------------ l.p
         * 12)       p ---------- q
         *    l.q ---------- l.p
         * 13)  q ---------- p
         *         l.q ---------- l.p
         * 14)  q ------------------------ p
         *         l.q ---------- l.p
         * 15)       q ---------- p
         *    l.q ------------------------ l.p
         * 16)       q ---------- p
         *    l.q ---------- l.p
         * }
         */
        if (l0.isIntersectedBy(l1.p)) {
            // Cases 1, 2, 5, 6, 14, 16
            if (l0.isIntersectedBy(l1.q)) {
                // Cases 2, 6, 14
                /**
                 * The line segments are effectively the same although the start
                 * and end points may be opposite.
                 */
                return l1;
            } else {
                // Cases 1, 5, 16
                if (l1.isIntersectedBy(l0.p)) {
                    // Cases 5
                    return new V3D_LineSegment(l1.p, l0.p);
                } else {
                    // Cases 1, 16
                    return new V3D_LineSegment(l1.p, l0.q);
                }
            }
        } else {
            // Cases 3, 4, 7, 8, 9, 10, 11, 12, 13, 15
            if (l0.isIntersectedBy(l1.q)) {
                // Cases 4, 8, 9, 10, 11
                if (l1.isIntersectedBy(l0.p)) {
                    // Cases 4, 11, 13
                    if (l1.isIntersectedBy(l0.q)) {
                        // Cases 11
                        return l0;
                    } else {
                        // Cases 4, 13
                        return new V3D_LineSegment(l0.p, l1.q);
                    }
                } else {
                    // Cases 8, 9, 10
                    if (l1.isIntersectedBy(l0.q)) {
                        // Cases 8, 9
                        return new V3D_LineSegment(l0.q, l1.q);
                    } else {
                        // Cases 10                      
                        return l1;
                    }
                }
            } else {
                // Cases 3, 7, 12, 15
                if (l1.isIntersectedBy(l0.p)) {
                    // Cases 3, 12, 15
                    if (l1.isIntersectedBy(l0.q)) {
                        // Cases 3, 15
                        return l0;
                    } else {
                        // Cases 12                 
                        return new V3D_LineSegment(l0.p, l1.p);
                    }
                } else {
                    // Cases 7
                    return l0;
                }
            }
        }
    }

//    @Override
//    public boolean isEnvelopeIntersectedBy(V3D_Line l) {
//        return getEnvelope().isIntersectedBy(l);
//    }
    /**
     * If the distance from a point to the line is less than the distance of the
     * point from either end of the line and the distance from either end of the
     * line is greater than the length of the line then the distance is the
     * shortest of the distances from the point to the points at either end of
     * the line segment. In all other cases, the distance is the distance
     * between the point and the line.
     *
     * @param p A point for which the minimum distance from {@code this} is
     * returned.
     *
     * @param oom The Order of Magnitude for the precision of the result.
     * @return The minimum distance between this and {@code p}.
     */
    @Override
    public BigDecimal getDistance(V3D_Point p, int oom) {
        BigDecimal d = super.getDistance(p, oom);
        BigDecimal l = this.getLength(oom);
        BigDecimal a = p.getDistance(this.p, oom);
        BigDecimal b = p.getDistance(this.q, oom);
        if (d.compareTo(a) == -1 && d.compareTo(b) == -1 && a.compareTo(l) == 1
                && b.compareTo(l) == 1) {
            return a.min(b);
        }
        return d;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy