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

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

There is a newer version: 0.8
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 ch.obermuhlner.math.big.BigRational;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Objects;
import uk.ac.leeds.ccg.math.Math_BigDecimal;
import uk.ac.leeds.ccg.math.Math_BigRationalSqrt;

/**
 * An envelope contains all the extreme values with respect to the X, Y and Z
 * axes. It is an axis aligned bounding box, which may have length of zero in
 * any direction. For a point the envelope is essentially the point. The
 * envelope may also be a line. In any case it has:
 * 
    *
  • a top ({@link #t}) aligned with {@link #yMax}
  • *
  • a bottom ({@link #b}) aligned with {@link #yMin}
  • *
  • a left ({@link #l}) aligned with {@link #xMin}
  • *
  • a right ({@link #r}) aligned with {@link #xMax}
  • *
  • a fore ({@link #f}) aligned with {@link #zMin}
  • *
  • a aft ({@link #a}) aligned with {@link #zMax}
  • *
* The following depiction of a bounding box indicate the location of the * different faces and also gives an abbreviated name of each point that * reflects these. This points are not stored explicitly in an instance of the * class with these names, but for a normal envelope (which is not a point or a * line or a plane), there are these 8 points stored in the rectangles that * represent each face. {@code * * z * y + * + / * | / * | / * | / * | * a * t * lta_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ rta * /| /| * / | / | * / | / | * / | / | * / | / | * / | / | * / | / | * / | / | * / | / | * / | / | * ltf /_ _ _ _ _ |_ _ _ _ _ _ _ _ _ _ _ /rtf | * | | | | * | | | | * - ---- l | | | | r ---- + x * | | | | * | lba|_ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _|rba * | / | / * | / | / * | / | / * | / | / * | / | / * | / | / * | / | / * | / | / * | / | / * | / | / * | / | / * lbf|/_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ |/rbf * * b * f * | * / | * / | * / | * - | * - * } * * @author Andy Turner * @version 1.0 */ public class V3D_Envelope extends V3D_Geometry implements V3D_FiniteGeometry { private static final long serialVersionUID = 1L; /** * The minimum x-coordinate. */ private final BigRational xMin; /** * The maximum x-coordinate. */ private final BigRational xMax; /** * The minimum y-coordinate. */ private final BigRational yMin; /** * The maximum y-coordinate. */ private final BigRational yMax; /** * The minimum z-coordinate. */ private final BigRational zMin; /** * The maximum z-coordinate. */ private final BigRational zMax; /** * The top face. */ protected final Geometry t; /** * The left face. */ protected final Geometry l; /** * The aft face. */ protected final Geometry a; /** * The right face. */ protected final Geometry r; /** * The fore face. */ protected final Geometry f; /** * The bottom face. */ protected final Geometry b; /** * * * * * * * * * * * * * * * *
Types
typedescription
0f, l, a, r, t, b Point
1f, l, a, r, t, b Rectangle
2f, a Point; l, r, t, b LineSegment
41, r Point; f, a, t, b LineSegment
6t, b Point; f, l, a, r LineSegment
3f, a LineSegment; l, r, t, b Rectangle
51, r LineSegment; f, a, t, b Rectangle
7t, b LineSegment; f, l, a, r Rectangle
*/ protected final int type; /** * @param e An envelope. */ public V3D_Envelope(V3D_Envelope e) { xMin = e.xMin; xMax = e.xMax; yMin = e.yMin; yMax = e.yMax; zMin = e.zMin; zMax = e.zMax; f = e.f; l = e.l; a = e.a; r = e.r; t = e.t; b = e.b; type = e.type; } /** * @param points The points used to form the envelop. */ public V3D_Envelope(V3D_Point... points) { int len = points.length; switch (len) { case 0: throw new RuntimeException("Cannot create envelope from an empty " + "collection of points."); case 1: xMin = points[0].x; xMax = points[0].x; yMin = points[0].y; yMax = points[0].y; zMin = points[0].z; zMax = points[0].z; f = new Point(points[0]); l = f; a = f; r = f; t = f; b = f; type = 1; break; default: BigRational xmin = points[0].x; BigRational xmax = points[0].x; BigRational ymin = points[0].y; BigRational ymax = points[0].y; BigRational zmin = points[0].z; BigRational zmax = points[0].z; for (int i = 1; i < points.length; i++) { xmin = BigRational.min(xmin, points[i].x); xmax = BigRational.max(xmax, points[i].x); ymin = BigRational.min(ymin, points[i].y); ymax = BigRational.max(ymax, points[i].y); zmin = BigRational.min(zmin, points[i].z); zmax = BigRational.max(zmax, points[i].z); } if (xmin.compareTo(xmax) == 0) { if (ymin.compareTo(ymax) == 0) { if (zmin.compareTo(zmax) == 0) { f = new Point(xmin, ymin, zmin); l = f; a = f; r = f; t = f; b = f; type = 0; } else { Point fp = new Point(xmin, ymin, zmin); Point ap = new Point(xmin, ymax, zmin); f = fp; l = new LineSegment(ap, fp); a = ap; r = new LineSegment(fp, ap); t = r; b = l; type = 2; } } else { Point bf = new Point(xmin, ymin, zmin); Point tf = new Point(xmin, ymax, zmin); if (zmin.compareTo(zmax) == 0) { f = new LineSegment(bf, tf); l = f; a = f; r = f; t = tf; b = bf; type = 4; } else { Point ta = new Point(xmin, ymax, zmax); Point ba = new Point(xmin, ymin, zmax); f = new LineSegment(bf, tf); l = new Rectangle(ba, ta, tf, bf); a = new LineSegment(ba, ta); r = new Rectangle(bf, tf, ta, ba); t = new LineSegment(tf, ta); b = new LineSegment(ba, bf); type = 3; } } } else { if (ymin.compareTo(ymax) == 0) { Point lf = new Point(xmin, ymin, zmin); Point rf = new Point(xmax, ymin, zmin); if (zmin.compareTo(zmax) == 0) { f = new LineSegment(lf, rf); l = lf; a = new LineSegment(rf, lf); r = rf; t = f; b = f; type = 6; } else { Point la = new Point(xmin, ymin, zmax); Point ra = new Point(xmax, ymin, zmax); f = new LineSegment(lf, rf); l = new LineSegment(la, lf); a = new LineSegment(ra, la); r = new LineSegment(rf, ra); t = new Rectangle(lf, la, ra, rf); b = new Rectangle(la, lf, rf, ra); type = 5; } } else { Point lbf = new Point(xmin, ymin, zmin); Point ltf = new Point(xmin, ymax, zmin); Point rtf = new Point(xmax, ymax, zmin); Point rbf = new Point(xmax, ymin, zmin); if (zmin.compareTo(zmax) == 0) { f = new Rectangle(lbf, ltf, rtf, rbf); l = new LineSegment(lbf, ltf); a = new Rectangle(rbf, rtf, ltf, lbf); r = new LineSegment(rbf, rtf); t = new LineSegment(ltf, rtf); b = new LineSegment(lbf, rbf); type = 7; } else { Point lba = new Point(xmin, ymin, zmax); Point lta = new Point(xmin, ymax, zmax); Point rta = new Point(xmax, ymax, zmax); Point rba = new Point(xmax, ymin, zmax); f = new Rectangle(lbf, ltf, rtf, rbf); l = new Rectangle(lba, lta, ltf, lbf); a = new Rectangle(rba, rta, lta, lba); r = new Rectangle(rbf, rtf, rta, rba); t = new Rectangle(ltf, lta, rta, rtf); b = new Rectangle(lba, lbf, rbf, rba); type = 1; } } } this.xMin = xmin; this.xMax = xmax; this.yMin = ymin; this.yMax = ymax; this.zMin = zmin; this.zMax = zmax; break; } } /** * @param x The x-coordinate of a point. * @param y The y-coordinate of a point. * @param z The z-coordinate of a point. */ public V3D_Envelope(BigRational x, BigRational y, BigRational z) { this(new V3D_Point(x, y, z)); } /** * @param xMin What {@link xMin} is set to. * @param xMax What {@link xMax} is set to. * @param yMin What {@link yMin} is set to. * @param yMax What {@link yMax} is set to. * @param zMin What {@link zMin} is set to. * @param zMax What {@link zMax} is set to. */ public V3D_Envelope(BigRational xMin, BigRational xMax, BigRational yMin, BigRational yMax, BigRational zMin, BigRational zMax) { this(new V3D_Point(xMin, yMin, zMin), new V3D_Point(xMax, yMax, zMax)); } @Override public String toString() { return this.getClass().getSimpleName() + "(xMin=" + getxMin().toString() + ", xMax=" + getxMax().toString() + ", yMin=" + getyMin().toString() + ", yMax=" + getyMax().toString() + ", zMin=" + getzMin().toString() + ", zMax=" + getzMax().toString() + ")"; } /** * @param v The vector to apply. * @return a new V3D_Envelope. */ @Override public V3D_Envelope apply(V3D_Vector v) { V3D_Point lbf = new V3D_Point(xMin, yMin, zMin).apply(v); V3D_Point ltf = new V3D_Point(xMin, yMin, zMax).apply(v); V3D_Point rtf = new V3D_Point(xMax, yMin, zMax).apply(v); V3D_Point rbf = new V3D_Point(xMax, yMin, zMin).apply(v); V3D_Point lba = new V3D_Point(xMin, yMax, zMin).apply(v); V3D_Point lta = new V3D_Point(xMin, yMax, zMax).apply(v); V3D_Point rba = new V3D_Point(xMax, yMax, zMin).apply(v); V3D_Point rta = new V3D_Point(xMax, yMax, zMax).apply(v); return new V3D_Envelope(lbf, ltf, rtf, rbf, lba, lta, rba, rta); } /** * @param e The V3D_Envelope to union with this. * @return an Envelope which is {@code this} union {@code e}. */ public V3D_Envelope union(V3D_Envelope e) { if (e.isContainedBy(this)) { return this; } else { return new V3D_Envelope( BigRational.min(e.getxMin(), getxMin()), BigRational.max(e.getxMax(), getxMax()), BigRational.min(e.getyMin(), getyMin()), BigRational.max(e.getyMax(), getyMax()), BigRational.min(e.getzMin(), getzMin()), BigRational.max(e.getzMax(), getzMax())); } } /** * If {@code e} touches, or overlaps then it intersects. * * @param e The Vector_Envelope2D to test for intersection. * @return {@code true} if this intersects with {@code e}. */ public boolean isIntersectedBy(V3D_Envelope e) { if (e.getxMax().compareTo(getxMin()) != -1 && e.getxMin().compareTo(getxMax()) != 1 && getxMax().compareTo(e.getxMin()) != -1 && getxMin().compareTo(e.getxMax()) != 1) { if (e.getyMax().compareTo(getyMin()) != -1 && e.getyMin().compareTo(getyMax()) != 1 && getyMax().compareTo(e.getyMin()) != -1 && getyMin().compareTo(e.getyMax()) != 1) { if (e.getzMax().compareTo(getzMin()) != -1 && e.getzMin().compareTo(getzMax()) != 1 && getzMax().compareTo(e.getzMin()) != -1 && getzMin().compareTo(e.getzMax()) != 1) { return true; } } } return false; } /** * Containment includes the boundary. So anything in or on the boundary is * contained. * * @param e V3D_Envelope * @return if this is contained by {@code e} */ public boolean isContainedBy(V3D_Envelope e) { return getxMax().compareTo(e.getxMax()) != 1 && getxMin().compareTo(e.getxMin()) != -1 && getyMax().compareTo(e.getyMax()) != 1 && getyMin().compareTo(e.getyMin()) != -1 && getzMax().compareTo(e.getzMax()) != 1 && getzMin().compareTo(e.getzMin()) != -1; } /** * @param p The point to test for intersection. * @return {@code true} if this intersects with {@code p} */ @Override public boolean isIntersectedBy(V3D_Point p) { return isIntersectedBy(p.x, p.y, p.z); } /** * @param x The x-coordinate of the point to test for intersection. * @param y The y-coordinate of the point to test for intersection. * @param z The z-coordinate of the point to test for intersection. * @return {@code true} if this intersects with {@code p} */ public boolean isIntersectedBy(BigRational x, BigRational y, BigRational z) { return x.compareTo(getxMin()) != -1 && x.compareTo(getxMax()) != 1 && y.compareTo(getyMin()) != -1 && y.compareTo(getyMax()) != 1 && z.compareTo(getzMin()) != -1 && z.compareTo(getzMax()) != 1; } /** * @param en The envelop to intersect. * @return {@code null} if there is no intersection; {@code en} if * {@code this.equals(en)}; otherwise returns the intersection. */ public V3D_Envelope getIntersection(V3D_Envelope en) { if (this.equals(en)) { return en; } if (!this.isIntersectedBy(en)) { return null; } return new V3D_Envelope( BigRational.max(getxMin(), en.getxMin()), BigRational.min(getxMax(), en.getxMax()), BigRational.max(getyMin(), en.getyMin()), BigRational.min(getyMax(), en.getyMax()), BigRational.max(getzMin(), en.getzMin()), BigRational.min(getzMax(), en.getzMax())); } /** * Returns {@code null} if {@code this} does not intersect {@code l}; * otherwise returns the intersection which is either a point or a line * segment. * * @param li The line to intersect. * @return {@code null} if there is no intersection; otherwise returns the * intersection. */ @Override public V3D_Geometry getIntersection(V3D_Line li) { switch (type) { case 0: V3D_Point fp = new V3D_Point((Point) f); if (li.isIntersectedBy(fp)) { return fp; } else { return null; } case 1: V3D_Geometry fil = (new V3D_Rectangle((Rectangle) f)) .getIntersection(li); V3D_Geometry lil = (new V3D_Rectangle((Rectangle) l)) .getIntersection(li); V3D_Geometry ail = (new V3D_Rectangle((Rectangle) a)) .getIntersection(li); if (fil == null) { if (lil == null) { V3D_Geometry ril = (new V3D_Rectangle((Rectangle) r)) .getIntersection(li); if (ail == null) { V3D_Geometry til = (new V3D_Rectangle((Rectangle) t)) .getIntersection(li); if (ril == null) { if (til == null) { return null; } else { V3D_Geometry bil = (new V3D_Rectangle((Rectangle) b)) .getIntersection(li); return new V3D_LineSegment((V3D_Point) til, (V3D_Point) bil); } } else { if (til == null) { V3D_Geometry bil = (new V3D_Rectangle((Rectangle) b)) .getIntersection(li); if (bil == null) { return null; } else { return V3D_FiniteGeometry.getGeometry((V3D_Point) ril, (V3D_Point) bil); } } else { return V3D_FiniteGeometry.getGeometry((V3D_Point) ril, (V3D_Point) til); } } } else { if (ril == null) { V3D_Geometry til = (new V3D_Rectangle((Rectangle) t)) .getIntersection(li); if (til == null) { V3D_Geometry bil = (new V3D_Rectangle((Rectangle) b)) .getIntersection(li); if (bil == null) { return null; } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) ail, (V3D_Point) bil); } } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) ail, (V3D_Point) til); } } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) ail, (V3D_Point) ril); } } } else { if (ail == null) { V3D_Geometry ril = (new V3D_Rectangle((Rectangle) r)) .getIntersection(li); if (ril == null) { V3D_Geometry til = (new V3D_Rectangle((Rectangle) t)) .getIntersection(li); V3D_Geometry bil = (new V3D_Rectangle((Rectangle) b)) .getIntersection(li); if (til == null) { if (bil == null) { return null; } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) lil, (V3D_Point) bil); } } else { if (bil == null) { return null; } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) lil, (V3D_Point) bil); } } } else { return new V3D_LineSegment((V3D_Point) lil, (V3D_Point) ril); } } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) lil, (V3D_Point) ail); } } } else { if (lil == null) { V3D_Geometry ril = (new V3D_Rectangle((Rectangle) r)) .getIntersection(li); if (ail == null) { if (ril == null) { V3D_Geometry til = (new V3D_Rectangle((Rectangle) t)) .getIntersection(li); if (til == null) { return null; } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) fil, (V3D_Point) til); } } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) fil, (V3D_Point) ril); } } else { return new V3D_LineSegment((V3D_Point) fil, (V3D_Point) ail); } } else { if (ail == null) { return V3D_FiniteGeometry.getGeometry( (V3D_Point) fil, (V3D_Point) lil); } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) fil, (V3D_Point) ail); } } } case 2: return new V3D_LineSegment((LineSegment) l).getIntersection(li); case 4: return new V3D_LineSegment((LineSegment) f).getIntersection(li); case 6: return new V3D_LineSegment((LineSegment) f).getIntersection(li); case 3: return new V3D_Rectangle((Rectangle) l).getIntersection(li); case 5: return new V3D_Rectangle((Rectangle) f).getIntersection(li); default: return new V3D_Rectangle((Rectangle) f).getIntersection(li); } } /** * @param li Line segment to intersect with {@code this}. * @return either a point or line segment which is the intersection of * {@code li} and {@code this}. * @param flag To distinguish this method from * {@link #getIntersection(uk.ac.leeds.ccg.v3d.geometry.V3D_Line)}. The * value is ignored. */ @Override public V3D_Geometry getIntersection(V3D_LineSegment li, boolean flag) { // Special case where both ends of li are within Envelope boolean lipi = isIntersectedBy(li.p); if (lipi && isIntersectedBy(li.q)) { return li; } switch (type) { case 0: V3D_Point fp = new V3D_Point((Point) f); if (li.isIntersectedBy(fp)) { return fp; } else { return null; } case 1: V3D_Geometry fil = (new V3D_Rectangle((Rectangle) f)) .getIntersection(li, flag); V3D_Geometry lil = (new V3D_Rectangle((Rectangle) l)) .getIntersection(li, flag); V3D_Geometry ail = (new V3D_Rectangle((Rectangle) a)) .getIntersection(li, flag); if (fil == null) { if (lil == null) { V3D_Geometry ril = (new V3D_Rectangle((Rectangle) r)) .getIntersection(li, flag); if (ail == null) { V3D_Geometry til = (new V3D_Rectangle((Rectangle) t)) .getIntersection(li, flag); if (ril == null) { V3D_Geometry bil = (new V3D_Rectangle((Rectangle) b)) .getIntersection(li, flag); if (til == null) { if (bil == null) { return null; } else { return getIntersection((V3D_Point) bil, li, lipi); } } else { return new V3D_LineSegment((V3D_Point) til, (V3D_Point) bil); } } else { if (til == null) { V3D_Geometry bil = (new V3D_Rectangle((Rectangle) b)) .getIntersection(li); if (bil == null) { return getIntersection((V3D_Point) ril, li, lipi); } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) ril, (V3D_Point) bil); } } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) ril, (V3D_Point) til); } } } else { if (ril == null) { V3D_Geometry til = (new V3D_Rectangle((Rectangle) t)) .getIntersection(li, flag); if (til == null) { V3D_Geometry bil = (new V3D_Rectangle((Rectangle) b)) .getIntersection(li, flag); if (bil == null) { return getIntersection((V3D_Point) ail, li, lipi); } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) ail, (V3D_Point) bil); } } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) ail, (V3D_Point) til); } } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) ail, (V3D_Point) ril); } } } else { if (ail == null) { V3D_Geometry ril = (new V3D_Rectangle((Rectangle) r)) .getIntersection(li, flag); if (ril == null) { V3D_Geometry til = (new V3D_Rectangle((Rectangle) t)) .getIntersection(li, flag); V3D_Geometry bil = (new V3D_Rectangle((Rectangle) b)) .getIntersection(li, flag); if (til == null) { if (bil == null) { return getIntersection((V3D_Point) lil, li, lipi); } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) lil, (V3D_Point) bil); } } else { if (bil == null) { return getIntersection((V3D_Point) lil, li, lipi); } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) lil, (V3D_Point) bil); } } } else { return new V3D_LineSegment((V3D_Point) lil, (V3D_Point) ril); } } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) lil, (V3D_Point) ail); } } } else { if (lil == null) { V3D_Geometry ril = (new V3D_Rectangle((Rectangle) r)) .getIntersection(li, flag); if (ail == null) { if (ril == null) { V3D_Geometry til = (new V3D_Rectangle((Rectangle) t)) .getIntersection(li, flag); if (til == null) { return getIntersection((V3D_Point) fil, li, lipi); } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) fil, (V3D_Point) til); } } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) fil, (V3D_Point) ril); } } else { return new V3D_LineSegment((V3D_Point) fil, (V3D_Point) ail); } } else { if (ail == null) { return getIntersection((V3D_Point) fil, li, lipi); } else { return V3D_FiniteGeometry.getGeometry( (V3D_Point) fil, (V3D_Point) ail); } } } case 2: return new V3D_LineSegment((LineSegment) l).getIntersection(li); case 4: return new V3D_LineSegment((LineSegment) f).getIntersection(li); case 6: return new V3D_LineSegment((LineSegment) f).getIntersection(li); case 3: return new V3D_Rectangle((Rectangle) l).getIntersection(li); case 5: return new V3D_Rectangle((Rectangle) f).getIntersection(li); default: return new V3D_Rectangle((Rectangle) f).getIntersection(li); } } private V3D_Geometry getIntersection(V3D_Point pi, V3D_LineSegment li, boolean lipi) { if (lipi) { return V3D_FiniteGeometry.getGeometry(li.p, pi); } else { return V3D_FiniteGeometry.getGeometry(li.q, pi); } } @Override public V3D_Envelope getEnvelope() { return this; } /** * Test for equality. * * @param g The V3D_Geometry to test for equality with this. * @return {@code true} iff this and e are equal. */ @Override public boolean equals(V3D_Geometry g) { if (g instanceof V3D_Envelope) { return equals((V3D_Envelope) g); } return false; } /** * Test for equality. * * @param e The V3D_Envelope to test for equality with this. * @return {@code true} iff this and e are equal. */ public boolean equals(V3D_Envelope e) { return this.getxMin().compareTo(e.getxMin()) == 0 && this.getxMax().compareTo(e.getxMax()) == 0 && this.getyMin().compareTo(e.getyMin()) == 0 && this.getyMax().compareTo(e.getyMax()) == 0 && this.getzMin().compareTo(e.getzMin()) == 0 && this.getzMax().compareTo(e.getzMax()) == 0; } @Override public boolean equals(Object o) { if (o instanceof V3D_Envelope) { return equals((V3D_Envelope) o); } return false; } @Override public int hashCode() { int hash = 7; hash = 43 * hash + Objects.hashCode(this.getxMin()); hash = 43 * hash + Objects.hashCode(this.getxMax()); hash = 43 * hash + Objects.hashCode(this.getyMin()); hash = 43 * hash + Objects.hashCode(this.getyMax()); hash = 43 * hash + Objects.hashCode(this.getzMin()); hash = 43 * hash + Objects.hashCode(this.getzMax()); return hash; } /** * @return the xMin */ public BigRational getxMin() { return xMin; } /** * @return the xMax */ public BigRational getxMax() { return xMax; } /** * @return the yMin */ public BigRational getyMin() { return yMin; } /** * @return the yMax */ public BigRational getyMax() { return yMax; } /** * @return the zMin */ public BigRational getzMin() { return zMin; } /** * @return the zMax */ public BigRational getzMax() { return zMax; } /** * Test if {@code this} is intersected by {@code li}. * * @param li The line to test for intersection. * @return {@code true} iff {@code this} is intersected by {@code li}. */ @Override public boolean isIntersectedBy(V3D_Line li) { switch (type) { case 0: return li.isIntersectedBy(new V3D_Point((Point) f)); case 1: /** * Each part of this testing could be done simultaneously. An * alternative method might test three orthogonal sides and the * additional corner that is on none of these sides. */ if (new V3D_Rectangle((Rectangle) f).isIntersectedBy(li)) { return true; } else if (new V3D_Rectangle((Rectangle) l).isIntersectedBy(li)) { return true; } else if (new V3D_Rectangle((Rectangle) a).isIntersectedBy(li)) { return true; } else if (new V3D_Rectangle((Rectangle) r).isIntersectedBy(li)) { return true; } else if (new V3D_Rectangle((Rectangle) t).isIntersectedBy(li)) { return true; } else { return new V3D_Rectangle((Rectangle) b).isIntersectedBy(li); } case 2: return new V3D_LineSegment((LineSegment) l).isIntersectedBy(li); case 4: return new V3D_LineSegment((LineSegment) f).isIntersectedBy(li); case 6: return new V3D_LineSegment((LineSegment) f).isIntersectedBy(li); case 3: return new V3D_Rectangle((Rectangle) l).isIntersectedBy(li); case 5: return new V3D_Rectangle((Rectangle) f).isIntersectedBy(li); default: return new V3D_Rectangle((Rectangle) f).isIntersectedBy(li); } } /** * * Test if {@code this} is intersected by {@code li}. * * @param li The line to test for intersection. * @param flag To distinguish this method from * {@link #isIntersectedBy(uk.ac.leeds.ccg.v3d.geometry.V3D_Line)}. The * value is ignored. * @return {@code true} iff {@code this} is intersected by {@code li}. */ @Override public boolean isIntersectedBy(V3D_LineSegment li, boolean flag) { V3D_Envelope le = li.getEnvelope(); if (le.isIntersectedBy(this)) { switch (type) { case 0: return li.isIntersectedBy(new V3D_Point((Point) f)); case 1: /** * Each part of this testing could be done simultaneously. * An alternative method might test three orthogonal sides * and the additional corner that is on none of these sides. */ if (new V3D_Rectangle((Rectangle) f).isIntersectedBy(li)) { return true; } else if (new V3D_Rectangle((Rectangle) l).isIntersectedBy(li)) { return true; } else if (new V3D_Rectangle((Rectangle) a).isIntersectedBy(li)) { return true; } else if (new V3D_Rectangle((Rectangle) r).isIntersectedBy(li)) { return true; } else if (new V3D_Rectangle((Rectangle) t).isIntersectedBy(li)) { return true; } else { return new V3D_Rectangle((Rectangle) b).isIntersectedBy(li); } case 2: return new V3D_LineSegment((LineSegment) l).isIntersectedBy(li); case 4: return new V3D_LineSegment((LineSegment) f).isIntersectedBy(li); case 6: return new V3D_LineSegment((LineSegment) f).isIntersectedBy(li); case 3: return new V3D_Rectangle((Rectangle) l).isIntersectedBy(li); case 5: return new V3D_Rectangle((Rectangle) f).isIntersectedBy(li); default: return new V3D_Rectangle((Rectangle) f).isIntersectedBy(li); } } return false; } @Override public boolean isEnvelopeIntersectedBy(V3D_Line l) { return isIntersectedBy(l); } /** * A point within the Envelope currently returns a distance of zero. This * could be changed in future to provide a negative distance which would be * the distance inside the envelope. To calculate the distance outside the * envelope, there are a number of different cases. The first cases are when * the closest point on the Envelope to the point {@code p} is one of the * corners. The next cases are when it is a point on one of the edges of * {@link #t}, {@link #b}, {@link #l}, {@link #r}, {@link #f}. The next * cases are when it is a point on one of the faces of {@link #t}, * {@link #b}, {@link #l}, {@link #r}, {@link #f}. Depending on the values * and the oom given. The distance may appear to be zero when it is in fact * greater than zero. To test if the distance is zero use * {@link #isIntersectedBy(uk.ac.leeds.ccg.v3d.geometry.V3D_Point)}. * * @param p The point to find the distance to/from. * @param oom The Order of Magnitude for the result precision. * @return The approximate or exact distance at the given {@code oom}. */ @Override public BigDecimal getDistance(V3D_Point p, int oom) { if (this.isIntersectedBy(p)) { return BigDecimal.ZERO; } // Special case where Envelope is infinitesimally small. if (l instanceof Point && t instanceof Point) { return ((Point) l).getDistance(new Point(p)).toBigDecimal(oom); } int xcmin = p.x.compareTo(xMin); int ycmin = p.y.compareTo(yMin); int zcmin = p.z.compareTo(zMin); if (xcmin == -1) { if (ycmin == -1) { if (zcmin == -1) { // lbf if (f instanceof Rectangle) { return ((Rectangle) f).p.getDistance(new Point(p)).toBigDecimal(oom); } else if (f instanceof LineSegment) { return ((LineSegment) f).p.getDistance(new Point(p)).toBigDecimal(oom); } else { return ((Point) f).getDistance(new Point(p)).toBigDecimal(oom); } } else { int zcmax = p.z.compareTo(zMax); if (zcmax == 1) { // bla if (l instanceof Rectangle) { return ((Rectangle) l).p.getDistance(new Point(p)).toBigDecimal(oom); } else if (l instanceof LineSegment) { return ((LineSegment) l).p.getDistance(new Point(p)).toBigDecimal(oom); } else { return ((Point) l).getDistance(new Point(p)).toBigDecimal(oom); } } else { // lba - lbf if (l instanceof Rectangle) { return new Line(((Rectangle) l).b).getDistance(new Point(p), oom); } else if (l instanceof LineSegment) { return ((Line) l).getDistance(new Point(p), oom); } else { return ((Point) l).getDistance(new Point(p), oom); } } } } else { int ycmax = p.y.compareTo(yMax); if (ycmax == 1) { if (zcmin == -1) { // ltf if (f instanceof Rectangle) { return ((Rectangle) f).q.getDistance(new Point(p), oom); } else if (f instanceof LineSegment) { return ((LineSegment) f).p.getDistance(new Point(p), oom); } else { return ((Point) f).getDistance(new Point(p), oom); } } else { int zcmax = p.z.compareTo(zMax); if (zcmax == 1) { // lta if (l instanceof Rectangle) { return ((Rectangle) l).q.getDistance(new Point(p), oom); } else if (l instanceof LineSegment) { return ((LineSegment) l).p.getDistance(new Point(p), oom); } else { return ((Point) l).getDistance(new Point(p), oom); } } else { // lta - ltf if (l instanceof Rectangle) { return new Line(((Rectangle) l).t).getDistance(new Point(p), oom); } else if (l instanceof LineSegment) { return ((Line) l).getDistance(new Point(p), oom); } else { return ((Point) l).getDistance(new Point(p), oom); } } } } else { if (zcmin == -1) { // lbf - ltf if (l instanceof Rectangle) { return new Line(((Rectangle) l).ri).getDistance(new Point(p), oom); } else if (l instanceof LineSegment) { return ((Line) l).getDistance(new Point(p), oom); } else { return ((Point) l).getDistance(new Point(p), oom); } } else { int zcmax = p.z.compareTo(zMax); if (zcmax == 1) { // lba - lta if (l instanceof Rectangle) { return new Line(((Rectangle) l).l).getDistance(new Point(p), oom); } else if (l instanceof LineSegment) { return ((Line) l).getDistance(new Point(p), oom); } else { return ((Point) l).getDistance(new Point(p), oom); } } else { // lba - lta - ltf - lbf if (l instanceof Rectangle) { return new Plane((Rectangle) l).getDistance(new Point(p), oom); } else if (l instanceof LineSegment) { return ((Line) l).getDistance(new Point(p), oom); } else { return ((Point) l).getDistance(new Point(p), oom); } } } } } } else { int xcmax = p.x.compareTo(xMax); if (xcmax == 1) { if (ycmin == -1) { if (zcmin == -1) { // rbf if (f instanceof Rectangle) { return ((Rectangle) f).s.getDistance(new Point(p), oom); } else if (f instanceof LineSegment) { return ((LineSegment) f).q.getDistance(new Point(p), oom); } else { return ((Point) f).getDistance(new Point(p), oom); } } else { int zcmax = p.z.compareTo(zMax); if (zcmax == 1) { // rba if (a instanceof Rectangle) { return ((Rectangle) a).p.getDistance(new Point(p), oom); } else if (a instanceof LineSegment) { return ((LineSegment) a).p.getDistance(new Point(p), oom); } else { return ((Point) a).getDistance(new Point(p), oom); } } else { // rbf - rba if (r instanceof Rectangle) { return new Line(((Rectangle) r).b).getDistance(new Point(p), oom); } else if (r instanceof LineSegment) { return ((Line) r).getDistance(new Point(p), oom); } else { return ((Point) r).getDistance(new Point(p), oom); } } } } else { int ycmax = p.y.compareTo(yMax); if (ycmax == 1) { if (zcmin == -1) { // rtf if (f instanceof Rectangle) { return ((Rectangle) f).r.getDistance(new Point(p), oom); } else if (f instanceof LineSegment) { return ((LineSegment) f).q.getDistance(new Point(p), oom); } else { return ((Point) f).getDistance(new Point(p), oom); } } else { int zcmax = p.z.compareTo(zMax); if (zcmax == 1) { // rta if (a instanceof Rectangle) { return ((Rectangle) a).q.getDistance(new Point(p), oom); } else if (a instanceof LineSegment) { return ((LineSegment) a).p.getDistance(new Point(p), oom); } else { return ((Point) a).getDistance(new Point(p), oom); } } else { // rtf - rta if (r instanceof Rectangle) { return new Line(((Rectangle) r).t).getDistance(new Point(p), oom); } else if (r instanceof LineSegment) { return ((Line) r).getDistance(new Point(p), oom); } else { return ((Point) r).getDistance(new Point(p), oom); } } } } else { if (zcmin == -1) { // rbf - rtf if (f instanceof Rectangle) { return new Line(((Rectangle) f).ri).getDistance(new Point(p), oom); } else if (f instanceof LineSegment) { return ((Line) f).getDistance(new Point(p), oom); } else { return ((Point) f).getDistance(new Point(p), oom); } } else { int zcmax = p.z.compareTo(zMax); if (zcmax == 1) { // rba - rta if (a instanceof Rectangle) { return new Line(((Rectangle) a).l).getDistance(new Point(p), oom); } else if (a instanceof LineSegment) { return ((Line) a).getDistance(new Point(p), oom); } else { return ((Point) a).getDistance(new Point(p), oom); } } else { // rbf - rtf - rta - rba if (r instanceof Rectangle) { return new Plane((Rectangle) r).getDistance(new Point(p), oom); } else if (r instanceof LineSegment) { return ((Line) r).getDistance(new Point(p), oom); } else { return ((Point) r).getDistance(new Point(p), oom); } } } } } } else { if (ycmin == -1) { if (zcmin == -1) { // lbf - rbf if (f instanceof Rectangle) { return new Line(((Rectangle) f).b).getDistance(new Point(p), oom); } else if (f instanceof LineSegment) { return ((Line) f).getDistance(new Point(p), oom); } else { return ((Point) f).getDistance(new Point(p), oom); } } else { int zcmax = p.z.compareTo(zMax); if (zcmax == 1) { // rba - lba if (a instanceof Rectangle) { return new Line(((Rectangle) a).b).getDistance(new Point(p), oom); } else if (a instanceof LineSegment) { return ((Line) a).getDistance(new Point(p), oom); } else { return ((Point) a).getDistance(new Point(p), oom); } } else { // lba - lbf - rbf - rba if (b instanceof Rectangle) { return new Plane((Rectangle) b).getDistance(new Point(p), oom); } else if (b instanceof LineSegment) { return ((Line) b).getDistance(new Point(p), oom); } else { return ((Point) b).getDistance(new Point(p), oom); } } } } else { int ycmax = p.y.compareTo(yMax); if (ycmax == 1) { if (zcmin == -1) { // ltf - rtf if (f instanceof Rectangle) { return ((Rectangle) f).t.getDistance(new Point(p), oom); } else if (f instanceof LineSegment) { return ((LineSegment) f).q.getDistance(new Point(p), oom); } else { return ((Point) f).getDistance(new Point(p), oom); } } else { int zcmax = p.z.compareTo(zMax); if (zcmax == 1) { // rta - lta if (a instanceof Rectangle) { return new Line(((Rectangle) a).t).getDistance(new Point(p), oom); } else if (a instanceof LineSegment) { return ((Line) a).getDistance(new Point(p), oom); } else { return ((Point) a).getDistance(new Point(p), oom); } } else { // ltf - lta - rta - rtf if (t instanceof Rectangle) { return new Plane((Rectangle) t).getDistance(new Point(p), oom); } else if (r instanceof LineSegment) { return ((Line) t).getDistance(new Point(p), oom); } else { return ((Point) t).getDistance(new Point(p), oom); } } } } else { if (zcmin == -1) { // lbf - ltf - rtf - rbf if (f instanceof Rectangle) { return new Plane((Rectangle) f).getDistance(new Point(p), oom); } else if (f instanceof LineSegment) { return ((Line) f).getDistance(new Point(p), oom); } else { return ((Point) f).getDistance(new Point(p), oom); } } else { int zcmax = p.z.compareTo(zMax); if (zcmax == 1) { // rba - rta - lta - lba if (a instanceof Rectangle) { return new Plane((Rectangle) a).getDistance(new Point(p), oom); } else if (a instanceof LineSegment) { return ((Line) a).getDistance(new Point(p), oom); } else { return ((Point) a).getDistance(new Point(p), oom); } } else { // lba - lbf - rbf - rba if (r instanceof Rectangle) { return new Plane((Rectangle) b).getDistance(new Point(p), oom); } else if (r instanceof LineSegment) { return ((LineSegment) b).getDistance(new Point(p), oom); } else { return ((Point) b).getDistance(new Point(p), oom); } } } } } } } } @Override public BigDecimal getDistance(V3D_Line l, int oom) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public BigDecimal getDistance(V3D_LineSegment l, int oom) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } /** * Abstract Geometry class for geometries aligning with axes. */ public abstract class Geometry implements Serializable { private static final long serialVersionUID = 1L; /** * Created a new Geometry. */ public Geometry() { } } /** * A basic Point class. */ public class Point extends Geometry { private static final long serialVersionUID = 1L; /** * The x coordinate. */ public BigRational x; /** * The y coordinate. */ public BigRational y; /** * The z coordinate. */ public BigRational z; /** * Create a new instance from {@code p}. * * @param p The point to duplicate */ public Point(V3D_Point p) { x = p.x; y = p.y; z = p.z; } /** * Create a new instance from {@code x}, {@code y}, {@code z}. * * @param x What {@link #x} is set to. * @param y What {@link #y} is set to. * @param z What {@link #z} is set to. */ public Point(BigRational x, BigRational y, BigRational z) { this.x = x; this.y = y; this.z = z; } /** * Create a new instance from {@code v}. * * @param v The vector. */ public Point(V3D_Vector v) { x = v.dx; y = v.dy; z = v.dz; } @Override public String toString() { return this.getClass().getSimpleName() + "(x=" + x.toString() + ", y=" + y.toString() + ", z=" + z.toString() + ")"; } /** * Get the distance between this and {@code p}. * * @param p A point. * @param oom The Order of Magnitude for the precision of the result. * @return The distance from {@code p} to this. */ public BigDecimal getDistance(Point p, int oom) { if (this.equals(p)) { return BigDecimal.ZERO; } return Math_BigDecimal.sqrt(getDistanceSquared(p).toBigDecimal(), oom, RoundingMode.HALF_UP); } /** * Get the distance between this and {@code p}. * * @param p A point. * @return The distance from {@code p} to this. */ public Math_BigRationalSqrt getDistance(Point p) { if (this.equals(p)) { return Math_BigRationalSqrt.ZERO; } return new Math_BigRationalSqrt(getDistanceSquared(p)); } /** * Get the distance squared between this and {@code p}. * * @param p A point. * @return The distance squared from {@code p} to this. */ public BigRational getDistanceSquared(Point p) { BigRational dx = this.x.subtract(p.x); BigRational dy = this.y.subtract(p.y); BigRational dz = this.z.subtract(p.z); return dx.pow(2).add(dy.pow(2)).add(dz.pow(2)); } } /** * A line that aligns with the axes (a reticule). */ public class Line extends Geometry { private static final long serialVersionUID = 1L; /** * A point defining the line. */ public final Point p; /** * A point defining the line. */ public final Point q; /** * The direction vector from {@link #p} in the direction of {@link #q}. */ public final V3D_Vector v; /** * {@code p} should not be equal to {@code q}. * * @param p What {@link #p} is set to. * @param q What {@link #q} is set to. */ public Line(Point p, Point q) { this.p = p; this.q = q; v = new V3D_Vector(q.x.subtract(p.x), q.y.subtract(p.y), q.z.subtract(p.z)); } /** * Create a new instance from {@code l} * * @param l Line to create from. */ public Line(Line l) { this.p = l.p; this.q = l.q; v = new V3D_Vector(q.x.subtract(p.x), q.y.subtract(p.y), q.z.subtract(p.z)); } /** * @param l The line to test this with to see if they are parallel. * @return {@code true} If this and {@code l} are parallel. */ public boolean isParallel(Line l) { return v.isScalarMultiple(l.v); } /** * @param pt A point to test for intersection. * @return {@code true} if p is on the line. */ public boolean isIntersectedBy(Point pt) { V3D_Vector ppt = new V3D_Vector(pt.x.subtract(p.x), pt.y.subtract(p.y), pt.z.subtract(p.z)); V3D_Vector cp = v.getCrossProduct(ppt); return cp.dx.isZero() && cp.dy.isZero() && cp.dz.isZero(); } /** * This computes the intersection and tests if it is {@code null} * * @param l The line to test if it isIntersectedBy with this. * @return {@code true} If this and {@code l} intersect. */ public boolean isIntersectedBy(Line l) { return getIntersection(l) != null; } /** * Get the intersection between two lines. * * @param l Another line. * @return The intersection between two lines or {@code null}. */ public Geometry getIntersection(Line l) { // If lines are coincident return line. if (this.isParallel(l)) { if (l.isIntersectedBy(p)) { return l; } else { return null; } } if (l.v.dx.compareTo(BigRational.ZERO) == 0) { if (l.v.dy.compareTo(BigRational.ZERO) == 0) { if (l.v.dz.compareTo(BigRational.ZERO) == 0) { if (isIntersectedBy(l.p)) { return l.p; } } } if (v.dx.compareTo(BigRational.ZERO) == 0) { //Point p = new Point(l.p.x, ); } } else { } return null; } /** * https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_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}. */ public BigDecimal getDistance(Point p, int oom) { V3D_Vector pv = new V3D_Vector(this.p, p); V3D_Vector vu = v.getUnitVector(oom - 2); return p.getDistance(new Point(vu.multiply(pv.getDotProduct(vu)) .add(new V3D_Vector(this.p))), oom); } /** * * @param l A line 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 l}. */ public BigDecimal getDistance(Line l, int oom) { if (isParallel(l)) { return l.getDistance(p, oom); } else { /** * Calculate the direction vector of the line connecting the * closest points by computing the cross product. */ V3D_Vector cp = v.getCrossProduct(l.v); /** * Calculate the delta from {@link #p} and l.p */ V3D_Vector delta = new V3D_Vector(p).subtract(new V3D_Vector(l.p)); BigRational m = BigRational.valueOf(cp.getMagnitude(oom - 2)); // d = cp.(delta)/m BigRational dp = cp.getDotProduct(delta); // m should only be zero if the lines are parallel. BigRational d = dp.divide(m); return Math_BigDecimal.round(d.toBigDecimal(), oom); } } } /** * An axis aligned line segment. */ public class LineSegment extends Line { private static final long serialVersionUID = 1L; /** * @param p What {@link #p} is set to. * @param q What {@link #q} is set to. */ public LineSegment(Point p, Point q) { super(p, q); } /** * @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(Point p) { 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; } /** * 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 Geometry getIntersection(Line l) { // Check if infinite lines intersect. Geometry i = l.getIntersection(new Line(this)); 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 Point) { if (isIntersectedBy((Point) i)) { return i; } else { return null; } } else if (i instanceof Line) { // If the lines are the same, then return this. return this; } else { // There is no intersection. return null; } } } /** * A plane aligning with the axes. */ public class Plane extends Geometry { private static final long serialVersionUID = 1L; /** * One of the points that defines the plane. */ protected final Point p; /** * One of the points that defines the plane. */ protected final Point q; /** * One of the points that defines the plane. */ protected final Point r; /** * The vector representing the move from {@link #p} to {@link #q}. */ protected final V3D_Vector pq; /** * The vector representing the move from {@link #q} to {@link #r}. */ protected final V3D_Vector qr; /** * The normal vector. (This is perpendicular to the plane and it's * direction is given by order in which the two vectors {@link #pq} and * {@link #qr} are used in a cross product calculation when the plane is * constructed. */ protected final V3D_Vector n; /** * Create a new instance. * * @param p The plane used to create this. */ public Plane(Plane p) { this(p.p, p.q, p.r, p.pq, p.qr, p.n); } /** * Create a new instance. * * @param p What {@link #p} is set to. * @param q What {@link #q} is set to. * @param r What {@link #r} is set to. * @param pq What {@link #pq} is set to. * @param qr What {@link #qr} is set to. * @param n What {@link #n} is set to. */ public Plane(Point p, Point q, Point r, V3D_Vector pq, V3D_Vector qr, V3D_Vector n) { this.p = p; this.q = q; this.r = r; this.pq = pq; this.qr = qr; this.n = n; } /** * Create a new instance. * * @param p What {@link #p} is set to. * @param q What {@link #q} is set to. * @param r What {@link #r} is set to. */ public Plane(Point p, Point q, Point r) { this(p, q, r, new V3D_Vector(p, q), new V3D_Vector(q, r), new V3D_Vector(p, q).getCrossProduct(new V3D_Vector(q, r))); } /** * @param l The line to test if it is parallel to this. * @return {@code true} if {@code this} is parallel to {@code l}. */ public boolean isParallel(Line l) { return n.getDotProduct(l.v).isZero(); } /** * @param l The line to test if it is on the plane. * @return {@code true} If {@code pt} is on the plane. */ public boolean isOnPlane(Line l) { return isIntersectedBy(l.p) && isIntersectedBy(l.q); } /** * @param pt The point to test if it is on the plane. * @return {@code true} If {@code pt} is on the plane. */ public boolean isIntersectedBy(Point pt) { BigRational d = n.dx.multiply(p.x.subtract(pt.x)) .add(n.dy.multiply(p.y.subtract(pt.y))) .add(n.dz.multiply(p.z.subtract(pt.z))); return d.compareTo(BigRational.ZERO) == 0; } /** * @param l The line to test for intersection with this. * @return {@code true} If this and {@code l} intersect. */ public boolean isIntersectedBy(Line l) { if (isParallel(l)) { if (!isOnPlane(l)) { return false; } } return true; } /** * https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection * * @param l The line to intersect with the plane. * @return The intersection of the line and the plane. This is either * {@code null} a line or a point. */ public Geometry getIntersection(Line l) { if (isParallel(l)) { if (isOnPlane(l)) { return l; } else { return null; } } // Are either of the points of l on the plane. if (isIntersectedBy(l.p)) { return l.p; } if (isIntersectedBy(l.q)) { return l.q; } BigRational num = new V3D_Vector(p, l.p).getDotProduct(n); BigRational den = l.v.getDotProduct(n); BigRational t = num.divide(den); return new Point(l.p.x.subtract(l.v.dx.multiply(t)), l.p.y.subtract(l.v.dy.multiply(t)), l.p.z.subtract(l.v.dz.multiply(t))); } /** * Get the distance between this and {@code pl}. * * @param p A point. * @param oom The order of magnitude of the precision. * @return The distance from {@code this} to {@code p}. */ public BigDecimal getDistance(Point p, int oom) { if (this.isIntersectedBy(p)) { return BigDecimal.ZERO; } V3D_Vector v = new V3D_Vector(p, this.p); V3D_Vector u = this.n.getUnitVector(oom); // MathContext mc = new MathContext(Math_BigRationalSqrt // .getOOM(BigRational.ONE, oom)); MathContext mc = new MathContext(6 - oom); return v.getDotProduct(u).abs().toBigDecimal(mc); } } /** * This rectangle is aligned with the axes. * * Like with {@link V3D_Rectangle}, the corner points {@link #p}, {@link #q}, * {@link #r}, {@link #s} are rectangular and {@link #pq} is assumed to be * orthogonal to {@link #qr}. The left of a rectangle {@link #l} is the line * segment from {@link #p} to {@link #q}. The top of a rectangle {@link #t} * is the line segment from {@link #q} to {@link #r}. The right of a * rectangle {@link #ri} is the line segment from {@link #r} to {@link #s}. * The bottom of a rectangle {@link #b} is the line segment from {@link #s} * to {@link #p}. The following depicts a generic rectangle {@code * t * q *-------------* r * | | * l | | ri * | | * p *-------------* s * b * } */ public class Rectangle extends Plane { private static final long serialVersionUID = 1L; /** * The other corner of the rectangle. The others are * {@link #p}, {@link #q}, and {@link #r}. */ protected final Point s; /** * For storing the vector from {@link #p} to {@link #q}. */ protected final LineSegment l; /** * For storing the line segment from {@link #q} to {@link #r}. */ protected final LineSegment t; /** * For storing the line segment from {@link #r} to {@link #s}. */ protected final LineSegment ri; /** * For storing the line segment from {@link #s} to {@link #p}. */ protected final LineSegment b; /** * @param p The bottom left corner of the rectangle. * @param q The top left corner of the rectangle. * @param r The top right corner of the rectangle. * @param s The bottom right corner of the rectangle. * @throws java.lang.RuntimeException iff the points do not define a * rectangle. */ public Rectangle(Point p, Point q, Point r, Point s) { super(p, q, r); this.s = s; //en = new V3D_Envelope(p, q, r, s); Not initialised here as it causes a StackOverflowError l = new LineSegment(p, q); t = new LineSegment(q, r); ri = new LineSegment(r, s); b = new LineSegment(s, p); } /** * @param l The line to intersect with. * @return A point or line segment. */ @Override public Geometry getIntersection(Line l) { Geometry g = new Plane(this).getIntersection(l); if (g == null) { return null; } else { if (g instanceof Point) { if (this.isIntersectedBy((Point) g)) { return g; } else { return null; } } else { Line li = (Line) g; /** * Get the intersection of the line and each edge of the * rectangle. */ Geometry ti = t.getIntersection(li); if (ti == null) { // Check ri, b, l Geometry rii = ri.getIntersection(li); if (rii == null) { // Check b, l Geometry bi = b.getIntersection(li); if (bi == null) { // Check l Geometry tli = this.l.getIntersection(li); if (tli == null) { return null; } else { return tli; } } else if (bi instanceof LineSegment) { return bi; } else { // Check l Geometry tli = this.l.getIntersection(li); if (tli == null) { return bi; } else { return new LineSegment((Point) bi, (Point) tli); } } } else if (rii instanceof LineSegment) { return rii; } else { // Check b, l Geometry bi = b.getIntersection(li); if (bi == null) { // Check l Geometry tli = this.l.getIntersection(li); if (tli == null) { return rii; } else { return new LineSegment((Point) rii, (Point) tli); } } else if (bi instanceof LineSegment) { return bi; } else { // Check l Geometry tli = this.l.getIntersection(li); if (tli == null) { Point riip = (Point) rii; Point bip = (Point) bi; if (riip.equals(bip)) { return bip; } else { return new LineSegment(riip, bip); } } else { return new LineSegment((Point) bi, (Point) tli); } } } } else if (ti instanceof LineSegment) { return ti; } else { // Check ri, b, l Geometry rii = ri.getIntersection(li); if (rii == null) { // Check b, l Geometry bi = b.getIntersection(li); if (bi == null) { // Check l Geometry tli = this.l.getIntersection(li); if (tli == null) { return ti; } else { Point tlip = (Point) tli; Point tip = (Point) ti; if (tlip.equals(tip)) { return tlip; } else { return new LineSegment(tlip, tip); } } } else if (bi instanceof LineSegment) { return bi; } else { return new LineSegment((Point) ti, (Point) bi); } } else { Point tip = (Point) ti; Point riip = (Point) rii; if (tip.equals(riip)) { // Check b, l Geometry sri = b.getIntersection(li); if (sri == null) { // Check l Geometry tli = this.l.getIntersection(li); if (tli == null) { return rii; } else { return new LineSegment(riip, (Point) tli); } } else if (sri instanceof LineSegment) { return sri; } else { return new LineSegment(riip, (Point) sri); } } else { return new LineSegment(riip, tip); } } } } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy