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

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

There is a newer version: 0.19
Show newest version
/*
 * Copyright 2020 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 java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import uk.ac.leeds.ccg.math.arithmetic.Math_BigDecimal;
import uk.ac.leeds.ccg.math.number.Math_BigRational;
import uk.ac.leeds.ccg.math.number.Math_BigRationalSqrt;

/**
 * For representing and processing triangles in 3D. A triangle has a non-zero
 * area. The corner points are {@link #pl}, {@link #q} and {@link #r}. The
 * following depicts a generic triangle {@code
 *                           pq
 *  p *- - - - - - - - - - - + - - - - - - - - - - -* q
 *     \~                   mpq                   ~/
 *      \  ~                 |                 ~  /
 *       \    ~              |              ~    /
 *        \      ~           |           ~      /
 *         \        ~        |        ~        /
 *          \          ~     |     ~          /
 *           \            ~  |  ~            /
 *       -n  -n  -n  -n  -n  c  +n  +n  +n  +n  +n  normal heading out from the page.
 *             \          ~  |  ~          /
 *              \      ~     |     ~      /
 *               \  ~        |        ~  /
 *                + mrp      |      mqr +
 *             rp  \         |         /  qr
 *                  \        |        /
 *                   \       |       /
 *                    \      |      /
 *                     \     |     /
 *                      \    |    /
 *                       \   |   /
 *                        \  |  /
 *                         \ | /
 *                          \|/
 *                           *
 *                           r
 * }
 *
 * @author Andy Turner
 * @version 1.0
 */
public class V3D_Triangle extends V3D_FiniteGeometry implements V3D_Face {

    private static final long serialVersionUID = 1L;

    /**
     * The plane of the triangle
     */
    public V3D_Plane pl;

    /**
     * Defines one of the corners of the triangle.
     */
    public V3D_Vector p;

    /**
     * Defines one of the corners of the triangle.
     */
    public V3D_Vector q;

    /**
     * Defines one of the corners of the triangle.
     */
    public V3D_Vector r;

    /**
     * For storing the line segment from {@link #getP()} to {@link #getQ()} for
     * a given Order of Magnitude and RoundingMode precision.
     */
    private V3D_LineSegment pq;

    /**
     * The Order of Magnitude for the precision of pq.
     */
    int pqoom;

    /**
     * The RoundingMode used for the calculation of pq.
     */
    RoundingMode pqrm;

    /**
     * For storing the line segment from {@link #getQ()} to {@link #getR()} for
     * a given Order of Magnitude and RoundingMode precision.
     */
    private V3D_LineSegment qr;

    /**
     * The Order of Magnitude for the precision of qr.
     */
    int qroom;

    /**
     * The RoundingMode used for the calculation of qr.
     */
    RoundingMode qrrm;

    /**
     * For storing the line segment from {@link #getR()} to {@link #getP()} for
     * a given Order of Magnitude and RoundingMode precision.
     */
    private V3D_LineSegment rp;

    /**
     * The Order of Magnitude for the precision of rp.
     */
    int rpoom;

    /**
     * The RoundingMode used for the calculation of rp.
     */
    RoundingMode rprm;

    /**
     * For storing the plane aligning with {@link #pq} in the direction of the
     * plane normal and with a normal orthogonal to the plane normal.
     */
    private V3D_Plane pqpl;

    /**
     * For storing the plane aligning with {@link #qr} in the direction of the
     * plane normal and with a normal orthogonal to the plane normal.
     */
    private V3D_Plane qrpl;

    /**
     * For storing the plane aligning with {@link #rp} in the direction of the
     * plane normal and with a normal orthogonal to the plane normal.
     */
    private V3D_Plane rppl;

//    /**
//     * For storing the midpoint between {@link #getP()} and {@link #getQ()} at
//     * a given Order of Magnitude and RoundingMode precision.
//     */
//    private V3D_Point mpq;
//
//    /**
//     * For storing the midpoint between {@link #getQ()} and {@link #getR()} at
//     * a given Order of Magnitude and RoundingMode precision.
//     */
//    private V3D_Point mqr;
//
//    /**
//     * For storing the midpoint between {@link #getR()} and {@link #getP()} at
//     * a given Order of Magnitude and RoundingMode precision.
//     */
//    private V3D_Point mrp;
//
//    /**
//     * For storing the centroid at a specific Order of Magnitude and 
//     * RoundingMode precision.
//     */
//    private V3D_Point c;
    /**
     * Creates a new triangle.
     *
     * @param t The triangle to clone.
     */
    public V3D_Triangle(V3D_Triangle t) {
        super(t.offset);
        pl = new V3D_Plane(t.pl);
        p = new V3D_Vector(t.p);
        q = new V3D_Vector(t.q);
        r = new V3D_Vector(t.r);
    }

    /**
     * Creates a new triangle. {@link #offset} is set to
     * {@link V3D_Vector#ZERO}.
     *
     * @param p What {@link #pl} is set to.
     * @param q What {@link #q} is set to.
     * @param r What {@link #r} is set to.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     */
    public V3D_Triangle(V3D_Vector p, V3D_Vector q,
            V3D_Vector r, int oom, RoundingMode rm) {
        this(V3D_Vector.ZERO, p, q, r, oom, rm);
    }

    /**
     * Creates a new triangle.
     *
     * @param offset What {@link #offset} is set to.
     * @param p What {@link #pl} is set to.
     * @param q What {@link #q} is set to.
     * @param r What {@link #r} is set to.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     */
    public V3D_Triangle(V3D_Vector offset, V3D_Vector p,
            V3D_Vector q, V3D_Vector r, int oom, RoundingMode rm) {
        super(offset);
        this.pl = new V3D_Plane(V3D_Vector.ZERO, p, q, r, oom, rm);
        this.p = p;
        this.q = q;
        this.r = r;
    }

    /**
     * Creates a new triangle.
     *
     * @param l A line segment representing one of the three edges of the
     * triangle.
     * @param r Defines the other point relative to l.offset that defines the
     * triangle.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     */
    public V3D_Triangle(V3D_LineSegment l, V3D_Vector r, int oom, RoundingMode rm) {
        this(l.offset, l.l.p, l.q, r, oom, rm);
    }

    /**
     * Creates a new triangle.
     *
     * @param l A line segment representing one of the three edges of the
     * triangle.
     * @param r Defines the other point relative to l.offset that defines the
     * triangle.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     */
    public V3D_Triangle(V3D_LineSegment l, V3D_Point r, int oom, RoundingMode rm) {
        this(l.offset, l.l.p, l.q, r.getVector(oom, rm).subtract(l.offset, oom, rm), oom, rm);
    }

    /**
     * Creates a new instance.
     *
     * @param p Used to initialise {@link #offset} and {@link #pl}.
     * @param q Used to initialise {@link #q}.
     * @param r Used to initialise {@link #r}.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     */
    public V3D_Triangle(V3D_Point p, V3D_Point q, V3D_Point r, int oom, RoundingMode rm) {
        super(p.offset);
        this.p = p.rel;
        this.q = q.getVector(oom, rm).subtract(p.offset, oom, rm);
        this.r = r.getVector(oom, rm).subtract(p.offset, oom, rm);
        this.pl = new V3D_Plane(p.offset, p.rel, this.q, this.r, oom, rm);
    }

    /**
     * @return A new point based on {@link #p} and {@link #offset}.
     */
    public final V3D_Point getP() {
        return new V3D_Point(offset, p);
    }

    /**
     * @return A new point based on {@link #q} and {@link #offset}.
     */
    public final V3D_Point getQ() {
        return new V3D_Point(offset, q);
    }

    /**
     * @return A new point based on {@link #r} and {@link #offset}.
     */
    public final V3D_Point getR() {
        return new V3D_Point(offset, r);
    }

    /**
     * For getting the line segment from {@link #getP()} to {@link #getQ()} with
     * at least oom precision given rm.
     *
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return Line segment from r to p.
     */
    public final V3D_LineSegment getPQ(int oom, RoundingMode rm) {
        if (pq == null) {
            initPQ(oom, rm);
        } else {
            if (oom < pqoom) {
                initPQ(oom, rm);
            } else {
                if (!pqrm.equals(rm)) {
                    initPQ(oom, rm);
                }
            }
        }
        return pq;
    }

    private void initPQ(int oom, RoundingMode rm) {
        pq = new V3D_LineSegment(offset, p, q, oom, rm);
        pqoom = oom;
        pqrm = rm;
    }

    /**
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return {@code q.subtract(p, oom, rm)}
     */
    public final V3D_Vector getPQV(int oom, RoundingMode rm) {
        return q.subtract(p, oom, rm);
    }

    /**
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return {@code r.subtract(q, oom, rm)}
     */
    public final V3D_Vector getQRV(int oom, RoundingMode rm) {
        return r.subtract(q, oom, rm);
    }

    /**
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return {@code p.subtract(r, oom, rm)}
     */
    public final V3D_Vector getRPV(int oom, RoundingMode rm) {
        return p.subtract(r, oom, rm);
    }

    /**
     * For getting the line segment from {@link #getQ()} to {@link #getR()} with
     * at least oom precision given rm.
     *
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return Line segment from r to p.
     */
    public final V3D_LineSegment getQR(int oom, RoundingMode rm) {
        if (qr == null) {
            initQR(oom, rm);
        } else {
            if (oom < qroom) {
                initQR(oom, rm);
            } else {
                if (!qrrm.equals(rm)) {
                    initQR(oom, rm);
                }
            }
        }
        return qr;
    }

    private void initQR(int oom, RoundingMode rm) {
        qr = new V3D_LineSegment(offset, q, r, oom, rm);
        qroom = oom;
        qrrm = rm;
    }

    /**
     * For getting the line segment from {@link #getR()} to {@link #getP()} with
     * at least oom precision given rm.
     *
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return Line segment from r to p.
     */
    public final V3D_LineSegment getRP(int oom, RoundingMode rm) {
        if (rp == null) {
            initRP(oom, rm);
        } else {
            if (oom < rpoom) {
                initRP(oom, rm);
            } else {
                if (!rprm.equals(rm)) {
                    initRP(oom, rm);
                }
            }
        }
        return rp;
    }

    private void initRP(int oom, RoundingMode rm) {
        rp = new V3D_LineSegment(offset, r, p, oom, rm);
        rpoom = oom;
        rprm = rm;
    }

    /**
     * For getting the plane through {@link #pq} in the direction of the normal.
     *
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The plane through {@link #pq} in the direction of the normal.
     */
    public V3D_Plane getPQPl(int oom, RoundingMode rm) {
        if (pqpl == null) {
            initPQPl(oom, rm);
        } else {
            if (oom < pqoom) {
                initPQPl(oom, rm);
            } else {
                if (!pqrm.equals(rm)) {
                    initPQPl(oom, rm);
                }
            }
        }
        return pqpl;
    }

    private void initPQPl(int oom, RoundingMode rm) {
        pq = getPQ(oom, rm);
        pqpl = new V3D_Plane(pq.getP(), pq.l.v.getCrossProduct(pl.n, oom, rm));
    }

    /**
     * For getting the plane through {@link #qr} in the direction of the normal.
     *
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The plane through {@link #qr} in the direction of the normal.
     */
    public V3D_Plane getQRPl(int oom, RoundingMode rm) {
        if (qrpl == null) {
            initQRPl(oom, rm);
        } else {
            if (oom < qroom) {
                initQRPl(oom, rm);
            } else {
                if (!qrrm.equals(rm)) {
                    initQRPl(oom, rm);
                }
            }
        }
        return qrpl;
    }

    private void initQRPl(int oom, RoundingMode rm) {
        qr = getQR(oom, rm);
        qrpl = new V3D_Plane(qr.getP(), qr.l.v.getCrossProduct(pl.n, oom, rm));
    }

    /**
     * For getting the plane through {@link #rp} in the direction of the normal.
     *
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The plane through {@link #rp} in the direction of the normal.
     */
    public V3D_Plane getRPPl(int oom, RoundingMode rm) {
        if (rppl == null) {
            initRPPl(oom, rm);
        } else {
            if (oom < rpoom) {
                initRPPl(oom, rm);
            } else {
                if (!rprm.equals(rm)) {
                    initRPPl(oom, rm);
                }
            }
        }
        return rppl;
    }

    private void initRPPl(int oom, RoundingMode rm) {
        rp = getRP(oom, rm);
        rppl = new V3D_Plane(rp.getP(), rp.l.v.getCrossProduct(pl.n, oom, rm));
    }

    @Override
    public V3D_Envelope getEnvelope(int oom, RoundingMode rm) {
        if (en == null) {
            en = new V3D_Envelope(oom, rm, getP(), getQ(), getR());
        }
        return en;
    }

    @Override
    public V3D_Point[] getPoints(int oom, RoundingMode rm) {
        V3D_Point[] re = new V3D_Point[3];
        re[0] = getP();
        re[1] = getQ();
        re[2] = getR();
        return re;
    }

    /**
     * @param pt The point to intersect with.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return A point or line segment.
     */
    public boolean isIntersectedBy(V3D_Point pt, int oom, RoundingMode rm) {
        if (getEnvelope(oom, rm).isIntersectedBy(pt, oom, rm)) {
            if (pl.isIntersectedBy(pt, oom, rm)) {
                return isIntersectedBy0(pt, oom, rm);
            }
        }
        return false;
    }

    /**
     * @param pt The point.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return {@code true} if this intersects with {@code pt}.
     */
    protected boolean isIntersectedBy0(V3D_Point pt, int oom, RoundingMode rm) {
        V3D_LineSegment tpq = getPQ(oom, rm);
        V3D_LineSegment tqr = getQR(oom, rm);
        V3D_LineSegment trp = getRP(oom, rm);
        if (tpq.isIntersectedBy(pt, oom, rm)
                || tqr.isIntersectedBy(pt, oom, rm)
                || trp.isIntersectedBy(pt, oom, rm)) {
            return true;
        }
        V3D_Vector ppt = new V3D_Vector(getP(), pt, oom, rm);
        V3D_Vector qpt = new V3D_Vector(getQ(), pt, oom, rm);
        V3D_Vector rpt = new V3D_Vector(getR(), pt, oom, rm);
        V3D_Vector cp = tpq.l.v.getCrossProduct(ppt, oom, rm);
        V3D_Vector cq = tqr.l.v.getCrossProduct(qpt, oom, rm);
        V3D_Vector cr = trp.l.v.getCrossProduct(rpt, oom, rm);
        /**
         * If cp, cq and cr are all in the same direction then pt intersects.
         */
        if (cp.dx.isNegative() == cq.dx.isNegative()
                && cp.dx.isNegative() == cr.dx.isNegative()) {
            if (cp.dy.isNegative() == cq.dy.isNegative()
                    && cp.dy.isNegative() == cr.dy.isNegative()) {
                if (cp.dz.isNegative() == cq.dz.isNegative()
                        && cp.dz.isNegative() == cr.dz.isNegative()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * The point pt aligns with this if it is on the same side of each plane
     * defined a triangle edge (with a normal given by the cross product of the
     * triangle normal and the edge line vector), and the other point of the
     * triangle. The plane normal may be imprecisely calculated. Greater
     * precision can be gained using a smaller oom.
     *
     * @param pt The point to check if it is in alignment.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return {@code true} iff pl is aligned with this.
     */
    public boolean isAligned(V3D_Point pt, int oom, RoundingMode rm) {
        if (getPQPl(oom, rm).isOnSameSide(pt, getR(), oom, rm)) {
            if (getQRPl(oom, rm).isOnSameSide(pt, getP(), oom, rm)) {
                if (getRPPl(oom, rm).isOnSameSide(pt, getQ(), oom, rm)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * A line segment aligns with this if both end points are aligned according
     * to
     * {@link #isAligned(uk.ac.leeds.ccg.v3d.geometry.V3D_Point, int, java.math.RoundingMode)}.
     *
     * @param l The line segment to check if it is in alignment.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return {@code true} iff l is aligned with this.
     */
    public boolean isAligned(V3D_LineSegment l, int oom, RoundingMode rm) {
        if (isAligned(l.getP(), oom, rm)) {
            return isAligned(l.getQ(), oom, rm);
        }
        return false;
    }

    /**
     * A triangle aligns with this if all points are aligned according to
     * {@link #isAligned(uk.ac.leeds.ccg.v3d.geometry.V3D_Point, int, java.math.RoundingMode)}.
     *
     * @param t The triangle to check if it is in alignment.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return {@code true} iff l is aligned with this.
     */
    public boolean isAligned(V3D_Triangle t, int oom, RoundingMode rm) {
        if (isAligned(t.getP(), oom, rm)) {
            if (isAligned(t.getQ(), oom, rm)) {
                return isAligned(t.getR(), oom, rm);
            }
        }
        return false;
    }

    @Override
    public BigDecimal getArea(int oom, RoundingMode rm) {
        int oomn2 = oom - 2;
        return getPQV(oomn2, rm).getCrossProduct(getRPV(oomn2, rm).reverse(), oomn2, rm)
                .getMagnitude(oomn2, rm).getSqrt(oom, rm).divide(Math_BigRational.TWO).toBigDecimal(oom, rm);
    }

    @Override
    public BigDecimal getPerimeter(int oom, RoundingMode rm) {
        int oomn2 = oom - 2;
        return Math_BigDecimal.round(getPQ(oomn2, rm).getLength(oomn2, rm).toBigDecimal(oomn2, rm)
                .add(getQR(oomn2, rm).getLength(oomn2, rm).toBigDecimal(oomn2, rm))
                .add(getRP(oomn2, rm).getLength(oomn2, rm).toBigDecimal(oomn2, rm)),
                oom, rm);
    }

    /**
     * @param l The line to intersect with.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return A point or line segment.
     */
    public V3D_FiniteGeometry getIntersection(V3D_Line l, int oom,
            RoundingMode rm) {
        V3D_Geometry i = pl.getIntersection(l, oom, rm);
        if (i == null) {
            return null;
        } else if (i instanceof V3D_Point ip) {
            if (isAligned(ip, oom, rm)) {
                return ip;
            } else {
                return null;
            }
        } else {
            /**
             * Get the intersection of the line and each edge of the triangle.
             */
            V3D_FiniteGeometry lpqi = getPQ(oom, rm).getIntersection(l, oom, rm);
            V3D_FiniteGeometry lqri = getQR(oom, rm).getIntersection(l, oom, rm);
            V3D_FiniteGeometry lrpi = getRP(oom, rm).getIntersection(l, oom, rm);
            if (lpqi == null) {
                if (lqri == null) {
                    return null; // This should not happen!
                } else {
                    if (lrpi == null) {
                        return lqri;
                    } else {
                        return getGeometry(((V3D_Point) lqri).getVector(oom, rm),
                                ((V3D_Point) lrpi).getVector(oom, rm), oom, rm);
                    }
                }
            } else if (lpqi instanceof V3D_Point lpqip) {
                if (lqri == null) {
                    if (lrpi == null) {
                        return lpqi;
                    } else {
                        return getGeometry(lpqip.getVector(oom, rm),
                                ((V3D_Point) lrpi).getVector(oom, rm), oom, rm);
                    }
                } else if (lqri instanceof V3D_Point lqrip) {
                    if (lrpi == null) {
                        return getGeometry(lqrip.getVector(oom, rm),
                                lpqip.getVector(oom, rm), oom, rm);
                    } else if (lrpi instanceof V3D_LineSegment) {
                        return lrpi;
                    } else {
                        return getGeometry(lpqip, lqrip, (V3D_Point) lrpi, oom, rm);
                    }
                } else {
                    return lqri;
                }
            } else {
                return lpqi;
            }
        }
    }

    /**
     * If {@code v1} and {@code v2} are the same, then return a point, otherwise
     * return a line segment. In both instance offset is set to
     * {@link V3D_Vector#ZERO}.
     *
     * @param v1 A vector.
     * @param v2 A vector.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return Either a line segment or a point.
     */
    public static V3D_FiniteGeometry getGeometry(
            V3D_Vector v1, V3D_Vector v2, int oom, RoundingMode rm) {
        if (v1.equals(v2)) {
            return new V3D_Point(v1);
        } else {
            return new V3D_LineSegment(v1, v2, oom, rm);
        }
    }

    /**
     * Get the intersection between the geometry and the line segment {@code l}.
     *
     * @param r The ray to intersect with.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The V3D_Geometry.
     */
    public V3D_FiniteGeometry getIntersection(V3D_Ray r, int oom,
            RoundingMode rm) {
        V3D_FiniteGeometry li = getIntersection(r.l, oom, rm);
        if (li == null) {
            return null;
        }
        if (li instanceof V3D_Point lip) {
            if (r.isAligned(lip, oom, rm)) {
                return lip;
            } else {
                return null;
            }
        }
        V3D_Plane rpl = r.getPl();
        return ((V3D_LineSegment) li).clip(rpl, r.l.getQ(oom, rm), oom, rm);
    }

    /**
     * Get the intersection between the geometry and the line segment {@code l}.
     *
     * @param l The line segment to intersect with.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The V3D_Geometry.
     */
    public V3D_FiniteGeometry getIntersection(V3D_LineSegment l, int oom,
            RoundingMode rm) {
        V3D_FiniteGeometry li = getIntersection(l.l, oom, rm);
        if (li == null) {
            return null;
        }
        if (li instanceof V3D_Point lip) {
            if (l.isAligned(lip, oom, rm)) {
                return lip;
            } else {
                return null;
            }
        }
        V3D_Point lp = l.getP();
        V3D_Point lq = l.getQ();
        if (isAligned(lp, oom, rm)) {
            if (isAligned(lq, oom, rm)) {
                return l;
            } else {
                return getIntersection0(l, lp, lq, oom, rm);
            }
        } else {
            if (isAligned(lq, oom, rm)) {
                return getIntersection0(l, lq, lp, oom, rm);
            } else {
                return li;
            }
        }
    }

    /**
     * Find the point of l to be the other end of the result. If l intersects
     * this, it can overshoot or undershoot, so this effectively clips the
     * result.
     *
     * @param l Line segment being intersected with this.
     * @param pt One end of the result.
     * @param opt The other end l.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return Either a point or a line segment.
     */
    private V3D_FiniteGeometry getIntersection0(V3D_LineSegment l, V3D_Point pt,
            V3D_Point opt, int oom, RoundingMode rm) {
        V3D_FiniteGeometry lipq = l.getIntersection(getPQ(oom, rm), oom, rm);
        V3D_FiniteGeometry liqr = l.getIntersection(getQR(oom, rm), oom, rm);
        V3D_FiniteGeometry lirp = l.getIntersection(getRP(oom, rm), oom, rm);
        V3D_Plane ptpl = new V3D_Plane(pt, l.l.v);
        if (lipq == null) {
            if (liqr == null) {
                if (lirp instanceof V3D_Point lirpp) {
                    if (ptpl.isOnSameSide(lirpp, opt, oom, rm)) {
                        return V3D_LineSegment.getGeometry(pt, lirpp, oom, rm);
                    } else {
                        return lirpp;
                    }
                } else {
                    return lirp;
                }
            } else {
                if (lirp == null) {
                    if (liqr instanceof V3D_Point liqrp) {
                        if (ptpl.isOnSameSide(liqrp, opt, oom, rm)) {
                            return V3D_LineSegment.getGeometry(pt, liqrp, oom, rm);
                        } else {
                            return liqrp;
                        }
                    } else {
                        return liqr;
                    }
                } else {
                    if (lirp instanceof V3D_LineSegment lirpl) {
                        return getGeometry(pt, lirpl.getP(), lirpl.getQ(), oom, rm);
                    }
                    if (liqr instanceof V3D_LineSegment liqrp) {
                        return getGeometry(pt, liqrp.getP(), liqrp.getQ(), oom, rm);
                    }
                    V3D_Point lirpp = (V3D_Point) lirp;
                    V3D_Point liqrp = (V3D_Point) liqr;
                    if (ptpl.isOnSameSide(lirpp, opt, oom, rm)) {
                        if (ptpl.isOnSameSide(liqrp, opt, oom, rm)) {
                            return getGeometry(pt, lirpp, liqrp, oom, rm);
                        } else {
                            return V3D_LineSegment.getGeometry(pt, lirpp, oom, rm);
                        }
                    } else {
                        if (ptpl.isOnSameSide(liqrp, opt, oom, rm)) {
                            return V3D_LineSegment.getGeometry(pt, liqrp, oom, rm);
                        } else {
                            return pt;
                        }
                    }
                }
            }
        } else {
            if (lipq instanceof V3D_LineSegment lipql) {
                return getGeometry(pt, lipql.getP(), lipql.getQ(), oom, rm);
            } else {
                if (liqr == null) {
                    if (lirp == null) {
                        V3D_Point lipqp = (V3D_Point) lipq;
                        if (ptpl.isOnSameSide(lipqp, opt, oom, rm)) {
                            return V3D_LineSegment.getGeometry(pt, lipqp, oom, rm);
                        } else {
                            return pt;
                        }
                    } else {
                        if (lirp instanceof V3D_LineSegment lirpl) {
                            return getGeometry(pt, lirpl.getP(), lirpl.getQ(), oom, rm);
                        }
                        V3D_Point lipqp = (V3D_Point) lipq;
                        V3D_Point lirpp = (V3D_Point) lirp;
                        if (ptpl.isOnSameSide(lipqp, opt, oom, rm)) {
                            if (ptpl.isOnSameSide(lirpp, opt, oom, rm)) {
                                return getGeometry(pt, lirpp, lipqp, oom, rm);
                            } else {
                                return V3D_LineSegment.getGeometry(pt, lipqp, oom, rm);
                            }
                        } else {
                            if (ptpl.isOnSameSide(lirpp, opt, oom, rm)) {
                                return V3D_LineSegment.getGeometry(pt, lirpp, oom, rm);
                            } else {
                                return pt;
                            }
                        }
                    }
                } else {
                    if (liqr instanceof V3D_LineSegment liqrp) {
                        return getGeometry(pt, liqrp.getP(), liqrp.getQ(), oom, rm);
                    }
                    if (lirp == null) {
                        V3D_Point lipqp = (V3D_Point) lipq;
                        V3D_Point liqrp = (V3D_Point) liqr;
                        if (ptpl.isOnSameSide(lipqp, opt, oom, rm)) {
                            if (ptpl.isOnSameSide(liqrp, opt, oom, rm)) {
                                return getGeometry(pt, liqrp, lipqp, oom, rm);
                            } else {
                                return V3D_LineSegment.getGeometry(pt, lipqp, oom, rm);
                            }
                        } else {
                            if (ptpl.isOnSameSide(liqrp, opt, oom, rm)) {
                                return V3D_LineSegment.getGeometry(pt, liqrp, oom, rm);
                            } else {
                                return pt;
                            }
                        }
                    } else {
                        if (lirp instanceof V3D_LineSegment lirpl) {
                            return getGeometry(pt, lirpl.getP(), lirpl.getQ(), oom, rm);
                        }
                        V3D_Point lipqp = (V3D_Point) lipq;
                        V3D_Point liqrp = (V3D_Point) liqr;
                        V3D_Point lirpp = (V3D_Point) lirp;
                        if (ptpl.isOnSameSide(lipqp, opt, oom, rm)) {
                            if (ptpl.isOnSameSide(liqrp, opt, oom, rm)) {
                                if (ptpl.isOnSameSide(lirpp, opt, oom, rm)) {
                                    throw new RuntimeException("Issue with Triangle-Traingle intersection.");
                                } else {
                                    return getGeometry(pt, liqrp, lipqp, oom, rm);
                                }
                            } else {
                                if (ptpl.isOnSameSide(lirpp, opt, oom, rm)) {
                                    return getGeometry(pt, lirpp, lipqp, oom, rm);
                                } else {
                                    return V3D_LineSegment.getGeometry(pt, lipqp, oom, rm);
                                }
                            }
                        } else {
                            if (ptpl.isOnSameSide(liqrp, opt, oom, rm)) {
                                if (ptpl.isOnSameSide(lirpp, opt, oom, rm)) {
                                    return getGeometry(pt, liqrp, lirpp, oom, rm);
                                } else {
                                    return V3D_LineSegment.getGeometry(pt, liqrp, oom, rm);
                                }
                            } else {
                                if (ptpl.isOnSameSide(lirpp, opt, oom, rm)) {
                                    return V3D_LineSegment.getGeometry(pt, lirpp, oom, rm);
                                } else {
                                    return pt;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Calculate and return the intersection between {@code this} and
     * {@code pl}. A question about how to do this:
     * https://stackoverflow.com/questions/3142469/determining-the-intersection-of-a-triangle-and-a-plane
     *
     * @param pl The plane to intersect with.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The intersection between {@code this} and {@code pl}.
     */
    public V3D_FiniteGeometry getIntersection(V3D_Plane pl, int oom,
            RoundingMode rm) {
        // Get intersection if this were a plane
        //V3D_Geometry pi = pl.getIntersection(this, oom);
        V3D_Geometry pi = pl.getIntersection(this.pl, oom, rm);
        if (pi == null) {
            return null;
        } else if (pi instanceof V3D_Plane) {
            return this;
        } else {
            // (pi instanceof V3D_Line)
            V3D_FiniteGeometry gPQ = pl.getIntersection(getPQ(oom, rm), oom, rm);
            if (gPQ == null) {
                V3D_FiniteGeometry gQR = pl.getIntersection(getQR(oom, rm), oom, rm);
                if (gQR == null) {
                    V3D_FiniteGeometry gRP = pl.getIntersection(getRP(oom, rm), oom, rm);
                    if (gRP == null) {
                        return null;
                    } else {
                        return gRP;
                    }
                } else {
                    V3D_Geometry gRP = pl.getIntersection(getRP(oom, rm), oom, rm);
                    if (gRP == null) {
                        return gQR;
                    } else {
                        if (gRP instanceof V3D_Point gRPp) {
                            return V3D_LineSegment.getGeometry((V3D_Point) gQR,
                                    gRPp, oom, rm);
                        } else {
                            return V3D_Triangle.getGeometry((V3D_LineSegment) gRP,
                                    (V3D_Point) gQR, oom, rm);
                        }
                    }
                }
            } else if (gPQ instanceof V3D_LineSegment) {
                return gPQ;
            } else {
                V3D_FiniteGeometry gQR = pl.getIntersection(getQR(oom, rm), oom, rm);
                if (gQR == null) {
                    V3D_FiniteGeometry gRP = pl.getIntersection(getRP(oom, rm), oom, rm);
                    if (gRP == null) {
                        return (V3D_Point) gPQ;
                    } else if (gRP instanceof V3D_LineSegment) {
                        return gRP;
                    } else {
                        return V3D_LineSegment.getGeometry((V3D_Point) gPQ,
                                (V3D_Point) gRP, oom, rm);
                    }
                } else {
                    if (gQR instanceof V3D_Point gQRp) {
                        return V3D_LineSegment.getGeometry((V3D_Point) gPQ, gQRp, oom, rm);
                    } else {
                        return gQR;
                    }
                }
            }
        }
    }

    /**
     * Computes and returns the intersection between {@code this} and {@code t}.
     * The intersection could be: null, a point, a line segment, a triangle, or
     * a V3D_ConvexHullCoplanar (with 4, 5, or 6 sides).
     *
     * @param t The triangle to test for intersection with this.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The intersection between {@code t} and {@code this} or
     * {@code null} if there is no intersection.
     */
    public V3D_FiniteGeometry getIntersection(V3D_Triangle t, int oom,
            RoundingMode rm) {
        if (getEnvelope(oom, rm).isIntersectedBy(t.getEnvelope(oom, rm), oom, rm)) {
            V3D_FiniteGeometry g = t.getIntersection(pl, oom, rm);
            if (g == null) {
                return g;
            } else {
                if (g instanceof V3D_Point pt) {
                    if (t.isIntersectedBy(pt, oom, rm)) {
                        return pt;
                    } else {
                        return null;
                    }
                } else if (g instanceof V3D_LineSegment l) {
                    return t.getIntersection(l, oom, rm);
                } else {
                    /**
                     * The two triangles are in the same plane Get intersections
                     * between the triangle edges. If there are none, then
                     * return t. If there are some, then in some cases the
                     * result is a single triangle, and in others it is a
                     * polygon which can be represented as a set of coplanar
                     * triangles.
                     */
                    // Check if vertices intersect
                    boolean pi = isIntersectedBy(t.getP(), oom, rm);
                    boolean qi = isIntersectedBy(t.getQ(), oom, rm);
                    boolean ri = isIntersectedBy(t.getR(), oom, rm);
                    if (pi && qi && ri) {
                        return t;
                    }
                    boolean pit = t.isIntersectedBy(getP(), oom, rm);
                    boolean qit = t.isIntersectedBy(getQ(), oom, rm);
                    boolean rit = t.isIntersectedBy(getR(), oom, rm);
                    if (pit && qit && rit) {
                        return this;
                    }
                    V3D_FiniteGeometry gpq = t.getIntersection(getPQ(oom, rm), oom, rm);
                    V3D_FiniteGeometry gqr = t.getIntersection(getQR(oom, rm), oom, rm);
                    V3D_FiniteGeometry grp = t.getIntersection(getRP(oom, rm), oom, rm);
                    if (gpq == null) {
                        if (gqr == null) {
//                                if (grp == null) {
//                                    // Return the smaller of the triangles
////                                    if (t.getArea(oom).compareTo(getArea(oom)) == 1) {
//                                    if (t.getPerimeter(oom).compareTo(getPerimeter(oom)) == 1) {
//                                        return this;
//                                    } else {
//                                        return t;
//                                    }
//                                } else {
                            return grp;
//                                }
                        } else if (gqr instanceof V3D_Point gqrp) {
                            if (grp == null) {
                                return gqr;
                            } else if (grp instanceof V3D_Point grpp) {
                                return V3D_LineSegment.getGeometry(
                                        gqrp, grpp, oom, rm);
                            } else {
                                V3D_LineSegment ls = (V3D_LineSegment) grp;
                                return getGeometry(gqrp, ls.getP(),
                                        ls.getQ(), oom, rm);
                            }
                        } else {
                            if (grp == null) {
                                return gqr;
                            } else if (grp instanceof V3D_Point grpp) {
                                V3D_LineSegment ls = (V3D_LineSegment) gqr;
                                return getGeometry(grpp, ls.getP(),
                                        ls.getQ(), oom, rm);
                            } else {
                                return getGeometry((V3D_LineSegment) gqr,
                                        (V3D_LineSegment) grp, oom, rm);
                            }
                        }
                    } else if (gpq instanceof V3D_Point gpqp) {
                        if (gqr == null) {
                            if (grp == null) {
                                return gpq;
                            } else if (grp instanceof V3D_Point grpp) {
                                return V3D_LineSegment.getGeometry(
                                        gpqp, grpp, oom, rm);
                            } else {
                                V3D_LineSegment ls = (V3D_LineSegment) grp;
                                return getGeometry(gpqp, ls.getP(),
                                        ls.getQ(), oom, rm);
                            }
                        } else if (gqr instanceof V3D_Point gqrp) {
                            if (grp == null) {
                                return gqr;
                            } else if (grp instanceof V3D_Point grpp) {
                                return V3D_LineSegment.getGeometry(gqrp, grpp, oom, rm); // Check!
                            } else {
                                V3D_LineSegment grpl = (V3D_LineSegment) grp;
                                return getGeometry(grpl, gqrp, gpqp, oom,
                                        rm);
                            }
                        } else {
                            V3D_LineSegment ls = (V3D_LineSegment) gqr;
                            if (grp == null) {
                                return getGeometry(ls, gpqp, oom, rm);
                            } else if (grp instanceof V3D_Point grpp) {
                                return getGeometry(ls, grpp, gpqp, oom, rm);
                            } else {
                                return null; // TODO: Figure out the geometry (point and two line segments).
                            }
                        }
                    } else {
                        V3D_LineSegment gpql = (V3D_LineSegment) gpq;
                        if (gqr == null) {
                            if (grp == null) {
                                return gpq;
                            } else if (grp instanceof V3D_Point grpp) {
                                return getGeometry(grpp, gpql.getP(),
                                        gpql.getQ(), oom, rm);
                            } else {
                                return getGeometry(gpql,
                                        (V3D_LineSegment) grp, oom, rm);
                            }
                        } else if (gqr instanceof V3D_Point gqrp) {
                            if (grp == null) {
                                if (gpql.isIntersectedBy(gqrp, oom, rm)) {
                                    return gpql;
                                } else {
                                    return new V3D_ConvexHullCoplanar(oom,
                                            rm, pl.n, gpql.getP(),
                                            gpql.getQ(), gqrp);
                                }
                            } else if (grp instanceof V3D_Point grpp) {
                                ArrayList pts = new ArrayList<>();
                                pts.add(gpql.getP());
                                pts.add(gpql.getQ());
                                pts.add(gqrp);
                                pts.add(grpp);
                                ArrayList pts2 = V3D_Point.getUnique(pts, oom, rm);
                                if (pts2.size() == 2) {
                                    return new V3D_LineSegment(pts2.get(0), pts2.get(1), oom, rm);
                                }
                                if (pts2.size() == 3) {
                                    return new V3D_Triangle(pts2.get(0), pts2.get(1), pts2.get(2), oom, rm);
                                }
                                return new V3D_ConvexHullCoplanar(oom,
                                        rm, pl.n, gpql.getP(),
                                        gpql.getQ(), gqrp, grpp);
                            } else {
                                V3D_LineSegment grpl = (V3D_LineSegment) grp;
                                return V3D_ConvexHullCoplanar.getGeometry(
                                        oom, rm, gpql.getP(),
                                        gpql.getQ(), gqrp, grpl.getP(),
                                        grpl.getQ());
                            }
                        } else {
                            V3D_LineSegment gqrl = (V3D_LineSegment) gqr;
                            if (grp == null) {
                                return V3D_ConvexHullCoplanar.getGeometry(
                                        oom, rm, gpql.getP(), gpql.getQ(),
                                        gqrl.getP(), gqrl.getQ());
                            } else if (grp instanceof V3D_Point grpp) {
                                return V3D_ConvexHullCoplanar.getGeometry(
                                        oom, rm, gpql.getP(),
                                        gpql.getQ(), gqrl.getP(),
                                        gqrl.getQ(), grpp);
                            } else {
                                V3D_LineSegment grpl = (V3D_LineSegment) grp;
                                return V3D_ConvexHullCoplanar.getGeometry(
                                        oom, rm, gpql.getP(),
                                        gpql.getQ(), gqrl.getP(),
                                        gqrl.getQ(), grpl.getP(),
                                        grpl.getQ());
                            }
                        }
                    }
                }
            }
        }
        return null;
    }

//    @Override
//    public boolean isEnvelopeIntersectedBy(V3D_Line l, int oom) {
//        return getEnvelope().isIntersectedBy(l, oom);
//    }
    /**
     * Calculate and return the centroid as a point. The original implementation
     * used intersection, but it is simpler to get the average of the x, y and z
     * coordinates from the points at each vertex.
     *
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The centroid point.
     */
    public V3D_Point getCentroid(int oom, RoundingMode rm) {
//        V3D_Point mpq = getPQ().getMidpoint(oom);
//        V3D_Point mqr = getQR().getMidpoint(oom);
//        V3D_Point mrp = getRP().getMidpoint(oom);
////        V3D_LineSegment lmpqr = new V3D_LineSegment(mpq, getRP().pl, oom);
////        V3D_LineSegment lmqrp = new V3D_LineSegment(mqr, getP(oom), oom);
//        V3D_LineSegment lmpqr = new V3D_LineSegment(offset, mpq.getRel(), getRP().getP(), oom);
//        V3D_LineSegment lmqrp = new V3D_LineSegment(offset, mqr.getRel(), getRP().getQ(), oom);
//        //V3D_LineSegment lmrpq = new V3D_LineSegment(mrp, getQ(oom), oom);
//        V3D_Point c0 = (V3D_Point) lmpqr.getIntersection(lmqrp, oom, true);
//        //V3D_Point c1 = (V3D_Point) lmpqr.getIntersection(lmrpq, oom, true);
//        //V3D_Point c2 = (V3D_Point) lmrpq.getIntersection(lmqrp, oom, true);
//        //System.out.println(toString());
//        //System.out.println("c0=" + c0.toString());
//        //System.out.println("c1=" + c1.toString());
//        //System.out.println("c2=" + c2.toString());
//        return c0;
//        return new V3D_Point(e, offset, getPV().add(getQV(), oom)
//                .add(getRV(), oom).divide(Math_BigRational.valueOf(3), oom));
        oom -= 6;
        Math_BigRational dx = (p.getDX(oom, rm).add(q.getDX(oom, rm))
                .add(r.getDX(oom, rm))).divide(3).round(oom, rm);
        Math_BigRational dy = (p.getDY(oom, rm).add(q.getDY(oom, rm))
                .add(r.getDY(oom, rm))).divide(3).round(oom, rm);
        Math_BigRational dz = (p.getDZ(oom, rm).add(q.getDZ(oom, rm))
                .add(r.getDZ(oom, rm))).divide(3).round(oom, rm);
        return new V3D_Point(offset, new V3D_Vector(dx, dy, dz));
    }

    /**
     * Test if two triangles are equal. Two triangles are equal if they have 3
     * coincident points, so even if the order is different and one is clockwise
     * and the other anticlockwise, or one faces the other way.
     *
     * @param t The other triangle to test for equality.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return {@code true} iff {@code this} is equal to {@code t}.
     */
    public boolean equals(V3D_Triangle t, int oom, RoundingMode rm) {
        V3D_Point tp = t.getP();
        V3D_Point thisp = getP();
        if (tp.equals(thisp, oom, rm)) {
            V3D_Point tq = t.getQ();
            V3D_Point thisq = getQ();
            if (tq.equals(thisq, oom, rm)) {
                return t.getR().equals(getR(), oom, rm);
            } else if (tq.equals(getR(), oom, rm)) {
                return t.getR().equals(thisq, oom, rm);
            } else {
                return false;
            }
        } else if (tp.equals(getQ(), oom, rm)) {
            V3D_Point tq = t.getQ();
            V3D_Point thisr = getR();
            if (tq.equals(thisr, oom, rm)) {
                return t.getR().equals(thisp, oom, rm);
            } else if (tq.equals(thisp, oom, rm)) {
                return t.getR().equals(thisr, oom, rm);
            } else {
                return false;
            }
        } else if (tp.equals(getR(), oom, rm)) {
            V3D_Point tq = t.getQ();
            if (tq.equals(thisp, oom, rm)) {
                return t.getR().equals(getQ(), oom, rm);
            } else if (tq.equals(getQ(), oom, rm)) {
                return t.getR().equals(thisp, oom, rm);
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    @Override
    public void translate(V3D_Vector v, int oom, RoundingMode rm) {
        super.translate(v, oom, rm);
        if (en != null) {
            en.translate(v, oom, rm);
        }
        if (pq != null) {
            pq.translate(v, oom, rm);
        }
        if (qr != null) {
            qr.translate(v, oom, rm);
        }
        if (rp != null) {
            rp.translate(v, oom, rm);
        }
        pl.translate(v, oom, rm);
        if (pqpl != null) {
            pqpl.translate(v, oom, rm);
        }
        if (qrpl != null) {
            qrpl.translate(v, oom, rm);
        }
        if (rppl != null) {
            rppl.translate(v, oom, rm);
        }
    }

    @Override
    public V3D_Triangle rotate(V3D_Line axis, Math_BigRational theta,
            int oom, RoundingMode rm) {
        return new V3D_Triangle(
                getP().rotate(axis, theta, oom, rm),
                getQ().rotate(axis, theta, oom, rm),
                getR().rotate(axis, theta, oom, rm), oom, rm);
    }

    /**
     * @param pad Padding
     * @return A String representation of this.
     */
    public String toString(String pad) {
        return pad + this.getClass().getSimpleName() + "(\n"
                + pad + " pl=(" + pl.toString(pad + " ") + "),\n"
                + pad + " offset=(" + this.offset.toString(pad + " ") + "),\n"
                + pad + " p=(" + this.p.toString(pad + " ") + "),\n"
                + pad + " q=(" + this.q.toString(pad + " ") + "),\n"
                + pad + " r=(" + this.r.toString(pad + " ") + "))";
    }

    /**
     * @param pad Padding
     * @return A simple String representation of this.
     */
    public String toStringSimple(String pad) {
        return pad + this.getClass().getSimpleName() + "(\n"
                + pad + " pl=(" + pl.toStringSimple(pad + " ") + "),\n"
                + pad + " offset=(" + this.offset.toStringSimple("") + "),\n"
                + pad + " p=(" + this.p.toStringSimple("") + "),\n"
                + pad + " q=(" + this.q.toStringSimple("") + "),\n"
                + pad + " r=(" + this.r.toStringSimple("") + "))";
    }

    @Override
    public String toString() {
        //return toString("");
        return toStringSimple("");
    }

    /**
     * If p, q and r are equal then the point is returned. If two of the points
     * are the same, then a line segment is returned. If all points are
     * different then a triangle is returned.
     *
     * @param p A point.
     * @param q Another possibly equal point.
     * @param r Another possibly equal point.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return either {@code pl} or {@code new V3D_LineSegment(pl, q)} or
     * {@code new V3D_Triangle(pl, q, r)}
     */
    public static V3D_FiniteGeometry getGeometry(V3D_Point p, V3D_Point q,
            V3D_Point r, int oom, RoundingMode rm) {
        if (p.equals(q, oom, rm)) {
            return V3D_LineSegment.getGeometry(p, r, oom, rm);
        } else {
            if (q.equals(r, oom, rm)) {
                return V3D_LineSegment.getGeometry(q, p, oom, rm);
            } else {
                if (r.equals(p, oom, rm)) {
                    return V3D_LineSegment.getGeometry(r, q, oom, rm);
                } else {
                    if (V3D_Line.isCollinear(oom, rm, p, q, r)) {
                        V3D_LineSegment pq = new V3D_LineSegment(p, q, oom, rm);
                        if (pq.isIntersectedBy(r, oom, rm)) {
                            return pq;
                        } else {
                            V3D_LineSegment qr = new V3D_LineSegment(q, r, oom, rm);
                            if (qr.isIntersectedBy(p, oom, rm)) {
                                return qr;
                            } else {
                                return new V3D_LineSegment(p, r, oom, rm);
                            }
                        }
                    }
                    return new V3D_Triangle(p, q, r, oom, rm);
//                    return new V3D_Triangle(pl.e, V3D_Vector.ZERO,
//                            pl.getVector(pl.e.oom),
//                            q.getVector(pl.e.oom), r.getVector(pl.e.oom));
                }
            }
        }
    }

    /**
     * Used in intersecting two triangles to give the overall intersection. If
     * l1, l2 and l3 are equal then the line segment is returned. If there are 3
     * unique points then a triangle is returned. If there are 4 or more unique
     * points, then a V3D_ConvexHullCoplanar is returned.
     *
     *
     * @param l1 A line segment.
     * @param l2 A line segment.
     * @param l3 A line segment.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return either {@code pl} or {@code new V3D_LineSegment(pl, q)} or
     * {@code new V3D_Triangle(pl, q, r)}
     */
    protected static V3D_FiniteGeometry getGeometry(V3D_LineSegment l1,
            V3D_LineSegment l2, V3D_LineSegment l3, int oom, RoundingMode rm) {
        V3D_Point l1p = l1.getP();
        V3D_Point l1q = l1.getQ();
        V3D_Point l2p = l2.getP();
        V3D_Point l2q = l2.getQ();
        V3D_Point l3p = l3.getP();
        V3D_Point l3q = l3.getQ();
        ArrayList points;
        {
            List pts = new ArrayList<>();
            pts.add(l1p);
            pts.add(l1q);
            pts.add(l2p);
            pts.add(l2q);
            pts.add(l3p);
            pts.add(l3q);
            points = V3D_Point.getUnique(pts, oom, rm);
        }
        int n = points.size();
        if (n == 2) {
            return l1;
        } else if (n == 3) {
            Iterator ite = points.iterator();
            return getGeometry(ite.next(), ite.next(), ite.next(), oom, rm);
        } else {
            V3D_Point[] pts = new V3D_Point[points.size()];
            int i = 0;
            for (var p : points) {
                pts[i] = p;
                i++;
            }
            V3D_Plane pl = new V3D_Plane(pts[0], pts[1], pts[2], oom, rm);
            return new V3D_ConvexHullCoplanar(oom, rm, pl.n, pts);
        }
//       // This way returned polygons.
//       } else if (n == 4) {
//            V3D_Triangle t1;
//            V3D_Triangle t2;
//            // Case: quadrangle (closed polygon with 4 sides)
//            V3D_Point illl2 = (V3D_Point) l1.getIntersection(l2, oom);
//            V3D_Point illl3 = (V3D_Point) l1.getIntersection(l3, oom);
//            V3D_Point il2l3 = (V3D_Point) l2.getIntersection(l3, oom);
//            if (illl2 == null) {
//                V3D_Point op1 = l1.l.getOtherPoint(illl3);
//                V3D_Point op2 = l2.l.getOtherPoint(il2l3);
//                t1 = new V3D_Triangle(op1, op2, l3p);
//                t2 = new V3D_Triangle(l3p, l3q, op2);
//            } else {
//                V3D_Point op1 = l1.l.getOtherPoint(illl2);
//                V3D_Point op3;
//                if (illl3 == null) {
//                    op3 = l3.l.getOtherPoint(il2l3);
//                    t1 = new V3D_Triangle(op1, op3, illl2);
//                    t2 = new V3D_Triangle(l3p, l3q, illl2);
//                } else {
//                    op3 = l3.l.getOtherPoint(illl3);
//                    t1 = new V3D_Triangle(op1, op3, illl2);
//                    t2 = new V3D_Triangle(l3p, l3q, illl2);
//                }
//            }
//            return new V3D_Polygon(t1, t2); // 
//            //throw new UnsupportedOperationException("Not supported yet.");
//        } else if (n == 5) {
//            V3D_Triangle t1;
//            V3D_Triangle t2;
//            V3D_Triangle t3;
//            // Case: convex pentagon (closed polygon with 5 sides)
//            V3D_Point illl2 = (V3D_Point) l1.getIntersection(l2, oom);
//            V3D_Point illl3 = (V3D_Point) l1.getIntersection(l3, oom);
//            V3D_Point il2l3 = (V3D_Point) l2.getIntersection(l3, oom);
//            // Find the two lines that intersect
//            if (illl2 == null) {
//                if (illl3 == null) {
//                    // l2 and l3 intersect
//                    V3D_Point op1 = l1.l.getOtherPoint(illl3);
//                    V3D_Point op3 = l3.l.getOtherPoint(illl3);
//                    t1 = new V3D_Triangle(op1, op3, illl3);
//                    t2 = new V3D_Triangle(op1, op3, l2p); // This might be twisted?
//                    t3 = new V3D_Triangle(op3, l2p, l2q);
//                } else {
//                    // l2 and l3 intersect
//                    V3D_Point op2 = l2.l.getOtherPoint(il2l3);
//                    V3D_Point op3 = l3.l.getOtherPoint(il2l3);
//                    t1 = new V3D_Triangle(op2, op3, il2l3);
//                    t2 = new V3D_Triangle(op2, op3, l1p); // This might be twisted?
//                    t3 = new V3D_Triangle(op3, l1p, l1q);
//                }
//            } else {
//                // l1 and l2 intersect
//                V3D_Point op1 = l1.l.getOtherPoint(illl2);
//                V3D_Point op2 = l2.l.getOtherPoint(illl2);
//                t1 = new V3D_Triangle(op1, op2, illl2);
//                t2 = new V3D_Triangle(op1, op2, l3p); // This might be twisted?
//                t3 = new V3D_Triangle(op2, l3p, l3q);
//            }
//            return new V3D_Polygon(t1, t2, t3);
//        } else {
//            // n = 6
//            V3D_Triangle t1;
//            V3D_Triangle t2;
//            V3D_Triangle t3;
//            V3D_Triangle t4;
//            /**
//             * Find the two points that are the minimum distance between any two
//             * lines. This will be an extra side to the triangle.
//             */
//            // dl1l2
//            Math_BigRational dl1pl2p = l1p.getDistanceSquared(l2p, oom);
//            Math_BigRational dl1pl2q = l1p.getDistanceSquared(l2q, oom);
//            Math_BigRational dl1ql2p = l1q.getDistanceSquared(l2p, oom);
//            Math_BigRational dl1ql2q = l1q.getDistanceSquared(l2q, oom);
//            // dl1l3
//            Math_BigRational dl1pl3p = l1p.getDistanceSquared(l3p, oom);
//            Math_BigRational dl1pl3q = l1p.getDistanceSquared(l3q, oom);
//            Math_BigRational dl1ql3p = l1q.getDistanceSquared(l3p, oom);
//            Math_BigRational dl1ql3q = l1q.getDistanceSquared(l3q, oom);
////            // dl2l3
////            Math_BigRational dl2pl3p = l2p.getDistanceSquared(l3p, oom);
////            Math_BigRational dl2pl3q = l2p.getDistanceSquared(l3q, oom);
////            Math_BigRational dl2ql3p = l2q.getDistanceSquared(l3p, oom);
////            Math_BigRational dl2ql3q = l2q.getDistanceSquared(l3q, oom);
//            if (dl1pl2p.compareTo(dl1pl2q) == -1) {
//                if (dl1pl2p.compareTo(dl1ql2q) == -1) {
//                    t1 = new V3D_Triangle(l1p, l1q, l2p);
//                    if (dl1pl2p.compareTo(dl1ql2q) == -1) {
//                        t2 = new V3D_Triangle(l3q, l2p, l2q);
//                        if (dl1ql3p.compareTo(dl1ql3q) == -1) {
//                            t3 = new V3D_Triangle(l3q, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        } else {
//                            t3 = new V3D_Triangle(l3p, l2p, l1q);
//                            t4 = new V3D_Triangle(l3p, l2p, l1q);
//                        }
//                    } else {
//                        t2 = new V3D_Triangle(l3q, l2p, l2q);
//                        if (dl1pl3p.compareTo(dl1pl3q) == -1) {
//                            t3 = new V3D_Triangle(l3q, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        } else {
//                            t3 = new V3D_Triangle(l3p, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        }
//                    }
//                } else {
//                    t1 = new V3D_Triangle(l1p, l1q, l2p);
//                    if (dl1pl2p.compareTo(dl1ql2q) == -1) {
//                        t2 = new V3D_Triangle(l3q, l2p, l2q);
//                        if (dl1ql3p.compareTo(dl1ql3q) == -1) {
//                            t3 = new V3D_Triangle(l1p, l2p, l3q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        } else {
//                            t3 = new V3D_Triangle(l3p, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        }
//                    } else {
//                        t2 = new V3D_Triangle(l3q, l2p, l2q);
//                        if (dl1pl3p.compareTo(dl1pl3q) == -1) {
//                            t3 = new V3D_Triangle(l3q, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        } else {
//                            t3 = new V3D_Triangle(l3p, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        }
//                    }
//                }
//            } else {
//                if (dl1pl2p.compareTo(dl1ql2q) == -1) {
//                    t1 = new V3D_Triangle(l1p, l1q, l2p);
//                    if (dl1pl2p.compareTo(dl1ql2q) == -1) {
//                        t2 = new V3D_Triangle(l3q, l2p, l2q);
//                        if (dl1ql3p.compareTo(dl1ql3q) == -1) {
//                            t3 = new V3D_Triangle(l3q, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        } else {
//                            t3 = new V3D_Triangle(l3p, l2p, l1q);
//                            t4 = new V3D_Triangle(l3p, l2p, l1q);
//                        }
//                    } else {
//                        t2 = new V3D_Triangle(l3q, l2p, l2q);
//                        if (dl1pl3p.compareTo(dl1pl3q) == -1) {
//                            t3 = new V3D_Triangle(l3q, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        } else {
//                            t3 = new V3D_Triangle(l3p, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        }
//                    }
//                } else {
//                    t1 = new V3D_Triangle(l1p, l1q, l2q);
//                    if (dl1pl2p.compareTo(dl1ql2q) == -1) {
//                        t2 = new V3D_Triangle(l3q, l2p, l2q);
//                        if (dl1ql3p.compareTo(dl1ql3q) == -1) {
//                            t3 = new V3D_Triangle(l1p, l2p, l3q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        } else {
//                            t3 = new V3D_Triangle(l3p, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        }
//                    } else {
//                        t2 = new V3D_Triangle(l3p, l2p, l2q);
//                        if (dl1pl3p.compareTo(dl1pl3q) == -1) {
//                            t3 = new V3D_Triangle(l3q, l2p, l1q);
//                            t4 = new V3D_Triangle(l3q, l2p, l1q);
//                        } else {
//                            t3 = new V3D_Triangle(l3p, l3q, l1q);
//                            t4 = new V3D_Triangle(l3p, l2p, l1q);
//                        }
//                    }
//                }
//            }
//            return new V3D_Polygon(t1, t2, t3, t4);
//        }
    }

    /**
     * Used in intersecting a triangle and a tetrahedron. If there are 3 unique
     * points then a triangle is returned. If there are 4 points, then a
     * V3D_ConvexHullCoplanar is returned.
     *
     * @param l1 A line segment.
     * @param l2 A line segment.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return either {@code pl} or {@code new V3D_LineSegment(pl, q)} or
     * {@code new V3D_Triangle(pl, q, r)}
     */
    protected static V3D_FiniteGeometry getGeometry2(V3D_LineSegment l1,
            V3D_LineSegment l2, int oom, RoundingMode rm) {
        V3D_Point l1p = l1.getP();
        V3D_Point l1q = l1.getQ();
        V3D_Point l2p = l2.getP();
        V3D_Point l2q = l2.getQ();
        ArrayList points;
        {
            List pts = new ArrayList<>();
            pts.add(l1p);
            pts.add(l1q);
            pts.add(l2p);
            pts.add(l2q);
            points = V3D_Point.getUnique(pts, oom, rm);
        }
        points.add(l1p);
        points.add(l1q);
        points.add(l2p);
        points.add(l2q);
        int n = points.size();
        if (n == 2) {
            return l1;
        } else if (n == 3) {
            Iterator ite = points.iterator();
            return getGeometry(ite.next(), ite.next(), ite.next(), oom, rm);
        } else {
            V3D_Point[] pts = new V3D_Point[points.size()];
            int i = 0;
            for (var p : points) {
                pts[i] = p;
                i++;
            }
            V3D_Plane pl = new V3D_Plane(pts[0], pts[1], pts[2], oom, rm);
            return new V3D_ConvexHullCoplanar(oom, rm, pl.n, pts);
        }
    }

    /**
     * This may be called when there is an intersection of two triangles. If l1
     * and l2 are two sides of a triangle, return the triangle.
     *
     * @param l1 A line segment and triangle edge.
     * @param l2 A line segment and triangle edge.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return a triangle for which l1 and l2 are edges
     */
    protected static V3D_FiniteGeometry getGeometry(V3D_LineSegment l1,
            V3D_LineSegment l2, int oom, RoundingMode rm) {
        V3D_Point pt = (V3D_Point) l1.getIntersection(l2, oom, rm);
        V3D_Point l1p = l1.getP();
        V3D_Point l2p = l2.getP();
        if (l1p.equals(pt, oom, rm)) {
            if (l2p.equals(pt, oom, rm)) {
                return new V3D_Triangle(pt, l1.getQ(), l2.getQ(), oom, rm);
            } else {
                return new V3D_Triangle(pt, l1.getQ(), l2p, oom, rm);
            }
        } else {
            if (l2p.equals(pt, oom, rm)) {
                return new V3D_Triangle(pt, l1p, l2.getQ(), oom, rm);
            } else {
                return new V3D_Triangle(pt, l1p, l2p, oom, rm);
            }
        }
    }

    /**
     * This may be called when there is an intersection of two triangles where l
     * is a side of a triangle and pl is a point.
     *
     * @param l A line segment.
     * @param p1 A point that is either not collinear to l or intersects l.
     * @param p2 A point that is either not collinear to l or intersects l.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return a triangle for which l is an edge and pl is a vertex.
     */
    protected static V3D_FiniteGeometry getGeometry(V3D_LineSegment l,
            V3D_Point p1, V3D_Point p2, int oom, RoundingMode rm) {
        if (l.isIntersectedBy(p1, oom, rm)) {
            return getGeometry(l, p2, oom, rm);
        } else {
            return new V3D_Triangle(p1, l.getP(), l.getQ(), oom, rm);
        }
    }

    /**
     * This may be called when there is an intersection of two triangles where l
     * is a side of a triangle and pl is a point that is not collinear to l.
     *
     * @param l A line segment.
     * @param p A point that is not collinear to l.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return a triangle for which l is an edge and pl is a vertex.
     */
    protected static V3D_FiniteGeometry getGeometry(V3D_LineSegment l,
            V3D_Point p, int oom, RoundingMode rm) {
        if (l.isIntersectedBy(p, oom, rm)) {
            return l;
        }
        return new V3D_Triangle(p, l.getP(), l.getQ(), oom, rm);
    }

    /**
     * For getting the point opposite a side of a triangle given the side.
     *
     * @param l a line segment either equal to one of the edges of this - null
     * null null null null null null null null null null     {@link #getPQ(int, java.math.RoundingMode)},
     * {@link #getQR(int, java.math.RoundingMode)} or
     * {@link #getRP(int, java.math.RoundingMode)}.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The point of {@code this} that does not intersect with {@code l}.
     */
    public V3D_Point getOpposite(V3D_LineSegment l, int oom, RoundingMode rm) {
        if (getPQ(oom, rm).equals(l, oom, rm)) {
            return getR();
        } else {
            if (getQR(oom, rm).equals(l, oom, rm)) {
                return getP();
            } else {
                return getQ();
            }
        }
    }

    /**
     * Get the minimum distance to {@code p}.
     *
     * @param p A point.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The distance squared to {@code p}.
     */
    public BigDecimal getDistance(V3D_Point p, int oom, RoundingMode rm) {
        return new Math_BigRationalSqrt(getDistanceSquared(p, oom, rm), oom, rm)
                .getSqrt(oom, rm).toBigDecimal(oom, rm);
    }

    /**
     * Get the minimum distance squared to {@code pt}.
     *
     * @param pt A point.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The distance squared to {@code p}.
     */
    public Math_BigRational getDistanceSquared(V3D_Point pt, int oom, RoundingMode rm) {
        if (pl.isIntersectedBy(pt, oom, rm)) {
            if (isIntersectedBy0(pt, oom, rm)) {
                return Math_BigRational.ZERO;
            } else {
                return getDistanceSquaredEdge(pt, oom, rm);
            }
        }
        V3D_Point poi = pl.getPointOfProjectedIntersection(pt, oom, rm);
        if (isAligned(poi, oom, rm)) {
            return poi.getDistanceSquared(pt, oom, rm).round(oom, rm);
        } else {
            return getDistanceSquaredEdge(pt, oom, rm);
        }
    }

    /**
     * Get the minimum distance squared to {@code pt} from the perimeter.
     *
     * @param pt A point.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The distance squared between pt and the nearest edge of this.
     */
    public Math_BigRational getDistanceSquaredEdge(V3D_Point pt, int oom,
            RoundingMode rm) {
        int oomn2 = oom - 2;
        Math_BigRational pqd2 = getPQ(oom, rm).getDistanceSquared(pt, oomn2, rm);
        Math_BigRational qrd2 = getQR(oom, rm).getDistanceSquared(pt, oomn2, rm);
        Math_BigRational rpd2 = getRP(oom, rm).getDistanceSquared(pt, oomn2, rm);
        return Math_BigRational.min(pqd2, qrd2, rpd2).round(oom, rm);
    }

    /**
     * Get the minimum distance to {@code l}.
     *
     * @param l A line.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The minimum distance to {@code l}.
     */
    public BigDecimal getDistance(V3D_Line l, int oom, RoundingMode rm) {
        return new Math_BigRationalSqrt(getDistanceSquared(l, oom, rm), oom, rm)
                .getSqrt(oom, rm).toBigDecimal(oom, rm);
    }

    /**
     * Get the minimum distance squared to {@code l}.
     *
     * @param l A line.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The minimum distance to {@code l}.
     */
    public Math_BigRational getDistanceSquared(V3D_Line l, int oom, RoundingMode rm) {
        Math_BigRational dpq2 = getPQ(oom, rm).getDistanceSquared(l, oom, rm);
        Math_BigRational dqr2 = getQR(oom, rm).getDistanceSquared(l, oom, rm);
        Math_BigRational drp2 = getRP(oom, rm).getDistanceSquared(l, oom, rm);
        return Math_BigRational.min(dpq2, dqr2, drp2);
    }

    /**
     * Get the minimum distance to {@code l}.
     *
     * @param l A line segment.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The minimum distance to {@code l}.
     */
    public BigDecimal getDistance(V3D_LineSegment l, int oom, RoundingMode rm) {
        return new Math_BigRationalSqrt(getDistanceSquared(l, oom, rm), oom, rm)
                .getSqrt(oom, rm).toBigDecimal(oom, rm);
    }

    /**
     * Get the minimum distance squared to {@code l}.
     *
     * @param l A line segment.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The minimum distance to {@code l}.
     */
    public Math_BigRational getDistanceSquared(V3D_LineSegment l, int oom,
            RoundingMode rm) {
        if (getIntersection(l, oom, rm) != null) {
            return Math_BigRational.ZERO;
        }
        Math_BigRational dlpq2 = l.getDistanceSquared(getPQ(oom, rm), oom, rm);
        Math_BigRational dlqr2 = l.getDistanceSquared(getQR(oom, rm), oom, rm);
        Math_BigRational dlrp2 = l.getDistanceSquared(getRP(oom, rm), oom, rm);
        Math_BigRational d2 = Math_BigRational.min(dlpq2, dlqr2, dlrp2);
        /**
         * For any end points of l that are aligned with this, calculate the
         * distances as these could be the minimum.
         */
        V3D_Point lp = l.getP();
        V3D_Point lq = l.getQ();
        if (isAligned(lp, oom, rm)) {
            d2 = Math_BigRational.min(d2, getDistanceSquared(lp, oom, rm));
        }
        if (isAligned(lq, oom, rm)) {
            d2 = Math_BigRational.min(d2, getDistanceSquared(lq, oom, rm));
        }
        return d2;
    }

    /**
     * Get the minimum distance to {@code p}.
     *
     * @param pl A plane.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The minimum distance squared to {@code p}.
     */
    public BigDecimal getDistance(V3D_Plane pl, int oom, RoundingMode rm) {
        return new Math_BigRationalSqrt(getDistanceSquared(pl, oom, rm), oom, rm)
                .getSqrt(oom, rm).toBigDecimal(oom, rm);
    }

    /**
     * Get the minimum distance squared to {@code pl}.
     *
     * @param pl A plane.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The minimum distance squared to {@code p}.
     */
    public Math_BigRational getDistanceSquared(V3D_Plane pl, int oom, RoundingMode rm) {
        Math_BigRational dplpq2 = pl.getDistanceSquared(getPQ(oom, rm), oom, rm);
        Math_BigRational dplqr2 = pl.getDistanceSquared(getQR(oom, rm), oom, rm);
//        Math_BigRational dplrp2 = pl.getDistanceSquared(getRP(oom, rm), oom, rm);
//        return Math_BigRational.min(dplpq2, dplqr2, dplrp2);
        return Math_BigRational.min(dplpq2, dplqr2);
    }

    /**
     * Get the minimum distance to {@code t}.
     *
     * @param t A triangle.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The minimum distance squared to {@code t}.
     */
    public BigDecimal getDistance(V3D_Triangle t, int oom, RoundingMode rm) {
        return new Math_BigRationalSqrt(getDistanceSquared(t, oom, rm), oom, rm)
                .getSqrt(oom, rm).toBigDecimal(oom, rm);
    }

    /**
     * Get the minimum distance squared to {@code t}.
     *
     * @param t A triangle.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return The minimum distance squared to {@code t}.
     */
    public Math_BigRational getDistanceSquared(V3D_Triangle t, int oom,
            RoundingMode rm) {
        if (getIntersection(t, oom, rm) != null) {
            return Math_BigRational.ZERO;
        }
        Math_BigRational dtpq2 = t.getDistanceSquared(getPQ(oom, rm), oom, rm);
        Math_BigRational dtqr2 = t.getDistanceSquared(getQR(oom, rm), oom, rm);
        Math_BigRational dtrp2 = t.getDistanceSquared(getRP(oom, rm), oom, rm);
        Math_BigRational dpq2 = getDistanceSquared(t.getPQ(oom, rm), oom, rm);
        Math_BigRational dqr2 = getDistanceSquared(t.getQR(oom, rm), oom, rm);
        Math_BigRational drp2 = getDistanceSquared(t.getRP(oom, rm), oom, rm);
        Math_BigRational d2 = Math_BigRational.min(dtpq2, dtqr2, dtrp2, dpq2,
                dqr2, drp2);
        /**
         * If any of the points of t are aligned with this, then these could be
         * closest.
         */
        V3D_Point pt = t.getP();
        if (isAligned(pt, oom, rm)) {
            d2 = Math_BigRational.min(d2, getDistanceSquared(pt, oom, rm));
        }
        pt = t.getQ();
        if (isAligned(pt, oom, rm)) {
            d2 = Math_BigRational.min(d2, getDistanceSquared(pt, oom, rm));
        }
        pt = t.getR();
        if (isAligned(pt, oom, rm)) {
            d2 = Math_BigRational.min(d2, getDistanceSquared(pt, oom, rm));
        }
        /**
         * If any of the points of this are aligned with t, then these could be
         * closest.
         */
        pt = getP();
        if (t.isAligned(pt, oom, rm)) {
            d2 = Math_BigRational.min(d2, t.getDistanceSquared(pt, oom, rm));
        }
        pt = getQ();
        if (t.isAligned(pt, oom, rm)) {
            d2 = Math_BigRational.min(d2, t.getDistanceSquared(pt, oom, rm));
        }
        pt = getR();
        if (t.isAligned(pt, oom, rm)) {
            d2 = Math_BigRational.min(d2, t.getDistanceSquared(pt, oom, rm));
        }
        return d2;
    }

    /**
     * For retrieving a Set of points that are the corners of the triangles.
     *
     * @param triangles The input.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return A Set of points that are the corners of the triangles.
     */
    //public static ArrayList getPoints(V3D_Triangle[] triangles) {
    public static V3D_Point[] getPoints(V3D_Triangle[] triangles, int oom, RoundingMode rm) {
        List s = new ArrayList<>();
        for (var t : triangles) {
            s.add(t.getP());
            s.add(t.getQ());
            s.add(t.getR());
        }
        ArrayList points = V3D_Point.getUnique(s, oom, rm);
        return points.toArray(V3D_Point[]::new);
    }

    /**
     * Clips this using pl and returns the part that is on the same side as pt.
     *
     * @param pl The plane that clips.
     * @param pt A point that is used to return the side of the clipped
     * triangle.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return null, the whole or a part of this.
     */
    public V3D_FiniteGeometry clip(V3D_Plane pl, V3D_Point pt, int oom, RoundingMode rm) {
        V3D_FiniteGeometry i = getIntersection(pl, oom, rm);
        V3D_Point ppt = this.pl.getP();
        if (i == null) {
            if (pl.isOnSameSide(ppt, pt, oom, rm)) {
                return this;
            } else {
                return null;
            }
        } else if (i instanceof V3D_Point ip) {
            /**
             * If at least two points of the triangle are on the same side of pl
             * as pt, then return this, otherwise return ip. As the calcualtion
             * of i is perhaps imprecise, then simply testing if ip equals one
             * of the triangle corner points and then testing another point to
             * see if it that is on the same side as pt might not work out
             * right!
             *
             */
            int poll = 0;
            if (pl.isOnSameSide(ppt, pt, oom, rm)) {
                poll++;
            }
            if (pl.isOnSameSide(getQ(), pt, oom, rm)) {
                poll++;
            }
            if (pl.isOnSameSide(getR(), pt, oom, rm)) {
                poll++;
            }
            if (poll > 1) {
                return this;
            } else {
                return ip;
            }
        } else {
            // i instanceof V3D_LineSegment
            V3D_LineSegment il = (V3D_LineSegment) i;
            V3D_Point qpt = getQ();
            V3D_Point rpt = getR();
            if (pl.isOnSameSide(ppt, pt, oom, rm)) {
                if (pl.isOnSameSide(qpt, pt, oom, rm)) {
                    if (pl.isOnSameSide(rpt, pt, oom, rm)) {
                        return this;
                    } else {
                        return getGeometry(il, getPQ(oom, rm), oom, rm);
                    }
                } else {
                    if (pl.isOnSameSide(rpt, pt, oom, rm)) {
                        return getGeometry(il, getRP(oom, rm), oom, rm);
                    } else {
                        return getGeometry(il, ppt, oom, rm);
                    }
                }
            } else {
                if (pl.isOnSameSide(qpt, pt, oom, rm)) {
                    if (pl.isOnSameSide(rpt, pt, oom, rm)) {
                        return getGeometry(il, getPQ(oom, rm), oom, rm);
                    } else {
                        return getGeometry(il, qpt, oom, rm);
                    }
                } else {
                    if (pl.isOnSameSide(rpt, pt, oom, rm)) {
                        return getGeometry(il, rpt, oom, rm);
                    } else {
                        return null;
                    }
                }
            }
        }
    }

    /**
     * Clips this using t.
     *
     * @param t The triangle to clip this with.
     * @param pt A point that is used to return the side of this that is
     * clipped.
     * @param oom The Order of Magnitude for the precision.
     * @param rm The RoundingMode if rounding is needed.
     * @return null, the whole or a part of this.
     */
    public V3D_FiniteGeometry clip(V3D_Triangle t, V3D_Point pt, int oom, RoundingMode rm) {
        V3D_Point tp = t.getP();
        V3D_Point tq = t.getQ();
        V3D_Point tr = t.getR();
        V3D_Vector n = t.pl.n;
        V3D_Point ppt = new V3D_Point(tp.offset.add(n, oom, rm), tp.rel);
        V3D_Plane ppl = new V3D_Plane(tp, tq, ppt, oom, rm);
        V3D_Point qpt = new V3D_Point(tq.offset.add(n, oom, rm), tq.rel);
        V3D_Plane qpl = new V3D_Plane(tq, tr, qpt, oom, rm);
        V3D_Point rpt = new V3D_Point(tr.offset.add(n, oom, rm), tr.rel);
        V3D_Plane rpl = new V3D_Plane(tr, tp, rpt, oom, rm);
        V3D_FiniteGeometry cppl = clip(ppl, pt, oom, rm);
        if (cppl == null) {
            return null;
        } else if (cppl instanceof V3D_Point) {
            return cppl;
        } else if (cppl instanceof V3D_LineSegment cppll) {
            V3D_FiniteGeometry cppllcqpl = cppll.clip(qpl, pt, oom, rm);
            if (cppllcqpl == null) {
                return null;
            } else if (cppllcqpl instanceof V3D_Point cppllcqplp) {
                return getGeometry(cppll, cppllcqplp, oom, rm);
                //return cppllcqpl;
            } else {
                return ((V3D_LineSegment) cppllcqpl).clip(rpl, pt, oom, rm);
            }
        } else if (cppl instanceof V3D_Triangle cpplt) {
            V3D_FiniteGeometry cppltcqpl = cpplt.clip(qpl, pt, oom, rm);
            if (cppltcqpl == null) {
                return null;
            } else if (cppltcqpl instanceof V3D_Point) {
                return cppltcqpl;
            } else if (cppltcqpl instanceof V3D_LineSegment cppltcqpll) {
                return cppltcqpll.clip(rpl, pt, oom, rm);
            } else if (cppltcqpl instanceof V3D_Triangle cppltcqplt) {
                return cppltcqplt.clip(rpl, pt, oom, rm);
            } else {
                V3D_ConvexHullCoplanar c = (V3D_ConvexHullCoplanar) cppltcqpl;
                return c.clip(rpl, pt, oom, rm);
            }
        } else {
            V3D_ConvexHullCoplanar c = (V3D_ConvexHullCoplanar) cppl;
            V3D_FiniteGeometry cc = c.clip(qpl, pt, oom, rm);
            if (cc == null) {
                return cc;
            } else if (cc instanceof V3D_Point) {
                return cc;
            } else if (cc instanceof V3D_LineSegment cppll) {
                V3D_FiniteGeometry cccqpl = cppll.clip(qpl, pt, oom, rm);
                if (cccqpl == null) {
                    return null;
                } else if (cccqpl instanceof V3D_Point) {
                    return cccqpl;
                } else {
                    return ((V3D_LineSegment) cccqpl).clip(rpl, pt, oom, rm);
                }
            } else if (cc instanceof V3D_Triangle ccct) {
                return ccct.clip(rpl, pt, oom, rm);
            } else {
                V3D_ConvexHullCoplanar ccc = (V3D_ConvexHullCoplanar) cc;
                return ccc.clip(rpl, pt, oom, rm);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy