uk.ac.leeds.ccg.v3d.geometry.V3D_ConvexHullCoplanar Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ccg-v3d Show documentation
Show all versions of ccg-v3d Show documentation
A Java Library for handling 3D spatial vector data.
/*
* Copyright 2022 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.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import uk.ac.leeds.ccg.math.number.Math_BigRational;
import uk.ac.leeds.ccg.math.number.Math_BigRationalSqrt;
//import java.util.ArrayList;
/**
* A class for representing and using coplanar convex hulls. These are a special
* type of polygon: They have no holes and all the angles are convex. Below is a
* basic algorithm for generating a convex hull from a set of coplanar points
* known as the "quick hull" algorithm (see
*
* https://en.wikipedia.org/wiki/Quickhull) :
*
* - Partition the points:
*
* - Calculate the distances between the points with the minimum and maximum
* x, the minimum and maximum y, and the minimum and maximum z values.
* - Choose the points that have the largest distance between them to define
* the dividing plane that is orthogonal to the plane of the polygon.
* - Let the points on one side of the dividing plane be one group and those
* on the other be the another group.
*
* - Add the two end points of the partition to the convex hull.
* - Deal with each group of points in turn.
* - If there is only one other point on a side of the partition then add it
* to the convex hull.
* - If there are more than one, then find the one with the biggest distance
* from the partition and add this to the convex hull.
* - We can now ignore all the other points that intersect the triangle given
* by the 3 points now in the convex hull.
* - Create a new plane dividing the remaining points on this side of the
* first dividing plane. Two points on the plane are the last point added to the
* convex hull and the closest point on the line defined by the other two points
* in the convex hull. The new dividing plane is orthogonal to the first
* dividing plane.
* - Let the points in this group that are on one side of the dividing plane
* be another group and those on the other be the another group.
* - Repeat the process dealing with each group in turn (Steps 3 to 9) in a
* depth first manner.
*
*
* @author Andy Turner
* @version 1.0
*/
public class V3D_ConvexHullCoplanar extends V3D_FiniteGeometry
implements V3D_Face {
private static final long serialVersionUID = 1L;
/**
* The collection of triangles.
*/
protected final ArrayList triangles;
/**
* The collection of points.
*/
protected final ArrayList points;
/**
* Create a new instance.
*
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @param triangles A non-empty list of coplanar triangles.
*/
public V3D_ConvexHullCoplanar(int oom, RoundingMode rm, V3D_Triangle... triangles) {
this(oom, rm, triangles[0].pl.n, V3D_Triangle.getPoints(triangles, oom, rm));
}
/**
* Create a new instance.
*
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @param n The normal for the plane.
* @param points A non-empty list of points in a plane given by n.
*/
public V3D_ConvexHullCoplanar(int oom, RoundingMode rm, V3D_Vector n, V3D_Point... points) {
super();
this.points = new ArrayList<>();
this.triangles = new ArrayList<>();
// Get a list of unique points.
ArrayList pts = V3D_Point.getUnique(Arrays.asList(points), oom, rm);
V3D_Vector v0 = pts.get(0).rel;
Math_BigRationalSqrt xmin = v0.dx;
Math_BigRationalSqrt xmax = v0.dx;
Math_BigRationalSqrt ymin = v0.dy;
Math_BigRationalSqrt ymax = v0.dy;
Math_BigRationalSqrt zmin = v0.dz;
Math_BigRationalSqrt zmax = v0.dz;
int xminIndex = 0;
int xmaxIndex = 0;
int yminIndex = 0;
int ymaxIndex = 0;
int zminIndex = 0;
int zmaxIndex = 0;
for (int i = 1; i < pts.size(); i++) {
V3D_Point pt = pts.get(i);
Math_BigRationalSqrt x = pt.rel.getDX();
Math_BigRationalSqrt y = pt.rel.getDY();
Math_BigRationalSqrt z = pt.rel.getDZ();
if (x.compareTo(xmin) == -1) {
xmin = x;
xminIndex = i;
}
if (x.compareTo(xmax) == 1) {
xmax = x;
xmaxIndex = i;
}
if (y.compareTo(ymin) == -1) {
ymin = y;
yminIndex = i;
}
if (y.compareTo(ymax) == 1) {
ymax = y;
ymaxIndex = i;
}
if (z.compareTo(zmin) == -1) {
zmin = z;
zminIndex = i;
}
if (z.compareTo(zmax) == 1) {
zmax = z;
zmaxIndex = i;
}
}
V3D_Point xminp = pts.get(xminIndex);
V3D_Point xmaxp = pts.get(xmaxIndex);
V3D_Point yminp = pts.get(yminIndex);
V3D_Point ymaxp = pts.get(ymaxIndex);
V3D_Point zminp = pts.get(zminIndex);
V3D_Point zmaxp = pts.get(zmaxIndex);
this.offset = xminp.offset;
if (xminIndex == xmaxIndex) {
V3D_LineSegment yd = new V3D_LineSegment(ymaxp, yminp, oom, rm);
V3D_LineSegment zd = new V3D_LineSegment(zmaxp, zminp, oom, rm);
Math_BigRational ydl2 = yd.getLength2(oom, rm);
Math_BigRational zdl2 = zd.getLength2(oom, rm);
if (ydl2.compareTo(zdl2) == 1) {
this.points.add(yminp);
this.points.add(ymaxp);
getConvexHull0(pts, yminp, ymaxp, n, 1, oom, rm);
} else {
this.points.add(zminp);
this.points.add(zmaxp);
getConvexHull0(pts, zminp, zmaxp, n, 1, oom, rm);
}
} else if (yminIndex == ymaxIndex) {
V3D_LineSegment xd = new V3D_LineSegment(xmaxp, xminp, oom, rm);
V3D_LineSegment zd = new V3D_LineSegment(zmaxp, zminp, oom, rm);
Math_BigRational xdl2 = xd.getLength2(oom, rm);
Math_BigRational zdl2 = zd.getLength2(oom, rm);
if (xdl2.compareTo(zdl2) == 1) {
this.points.add(xminp);
this.points.add(xmaxp);
getConvexHull0(pts, xminp, xmaxp, n, 1, oom, rm);
} else {
this.points.add(zminp);
this.points.add(zmaxp);
getConvexHull0(pts, zminp, zmaxp, n, 1, oom, rm);
}
} else if (zminIndex == zmaxIndex) {
V3D_LineSegment xd = new V3D_LineSegment(xmaxp, xminp, oom, rm);
V3D_LineSegment yd = new V3D_LineSegment(ymaxp, yminp, oom, rm);
Math_BigRational xdl2 = xd.getLength2(oom, rm);
Math_BigRational ydl2 = yd.getLength2(oom, rm);
if (xdl2.compareTo(ydl2) == 1) {
this.points.add(xminp);
this.points.add(xmaxp);
getConvexHull0(pts, xminp, xmaxp, n, 1, oom, rm);
} else {
this.points.add(yminp);
this.points.add(ymaxp);
getConvexHull0(pts, yminp, ymaxp, n, 1, oom, rm);
}
} else {
V3D_LineSegment xd = new V3D_LineSegment(xmaxp, xminp, oom, rm);
V3D_LineSegment yd = new V3D_LineSegment(ymaxp, yminp, oom, rm);
V3D_LineSegment zd = new V3D_LineSegment(zmaxp, zminp, oom, rm);
Math_BigRational xdl2 = xd.getLength2(oom, rm);
Math_BigRational ydl2 = yd.getLength2(oom, rm);
Math_BigRational zdl2 = zd.getLength2(oom, rm);
if (xdl2.compareTo(ydl2) == 1) {
if (xdl2.compareTo(zdl2) == 1) {
this.points.add(xminp);
this.points.add(xmaxp);
getConvexHull0(pts, xminp, xmaxp, n, 1, oom, rm);
} else {
this.points.add(zminp);
this.points.add(zmaxp);
getConvexHull0(pts, zminp, zmaxp, n, 1, oom, rm);
}
} else {
if (ydl2.compareTo(zdl2) == 1) {
this.points.add(yminp);
this.points.add(ymaxp);
getConvexHull0(pts, yminp, ymaxp, n, 1, oom, rm);
} else {
this.points.add(zminp);
this.points.add(zmaxp);
getConvexHull0(pts, zminp, zmaxp, n, 1, oom, rm);
}
}
}
V3D_Point pt = this.points.get(0);
for (int i = 1; i < this.points.size() - 1; i++) {
V3D_Point qt = this.points.get(i);
V3D_Point rt = this.points.get(i + 1);
triangles.add(new V3D_Triangle(pt, qt, rt, oom, rm));
}
}
/**
* Create a new instance.
*
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @param gs The input convex hulls.
*/
public V3D_ConvexHullCoplanar(int oom, RoundingMode rm, V3D_ConvexHullCoplanar... gs) {
this(oom, rm, gs[0].triangles.get(0).pl.n, V3D_FiniteGeometry.getPoints(oom, rm, gs));
}
/**
* Create a new instance.
*
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @param ch The convex hull to add to the convex hull with t.
* @param t The triangle used to set the normal and to add to the convex
* hull with ch.
*/
public V3D_ConvexHullCoplanar(int oom, RoundingMode rm, V3D_ConvexHullCoplanar ch, V3D_Triangle t) {
this(oom, rm, ch.triangles.get(0).pl.n, V3D_FiniteGeometry.getPoints(oom, rm, ch, t));
}
@Override
public V3D_Point[] getPoints(int oom, RoundingMode rm) {
int np = points.size();
V3D_Point[] re = new V3D_Point[np];
for (int i = 0; i < np; i++) {
re[i] = new V3D_Point(points.get(i));
}
return re;
}
@Override
public String toString() {
String s = this.getClass().getName() + "(";
Iterator ite = points.iterator();
s += ite.next().toString();
while (ite.hasNext()) {
s += ", " + ite.next();
}
s += ")";
return s;
}
/**
* Check if {@code this} is equal to {@code i}.
*
* @param c An instance to compare for equality.
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return {@code true} iff all the triangles are the same.
*/
public boolean equals(V3D_ConvexHullCoplanar c, int oom, RoundingMode rm) {
HashSet indexes = new HashSet<>();
for (var x : points) {
boolean found = false;
for (int i = 0; i < c.points.size(); i++) {
if (x.equals(c.points.get(i), oom, rm)) {
found = true;
indexes.add(i);
break;
}
}
if (!found) {
return false;
}
}
for (int i = 0; i < c.points.size(); i++) {
if (!indexes.contains(i)) {
boolean found = false;
for (var x : points) {
if (x.equals(c.points.get(i), oom, rm)) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
}
return true;
// /**
// * The triangularisation of this and i might be different and the number
// * of points in each might be different, but the areas they define might
// * be the same. For the areas to be the same each triangle from each
// * must either be in the other, or it must fully intersect the other.
// */
// if (!this.triangles.get(0).pl.equals(i.triangles.get(0).pl, oom, rm)) {
// // If they are not in the same plane, they are unequal!
// return false;
// }
// for (V3D_Triangle t : triangles) {
// V3D_Geometry g = i.getIntersection(t, oom, rm);
// if (g instanceof V3D_Triangle gt) {
// if (!t.equals(gt, oom, rm)) {
//// System.out.println(gt);
//// System.out.println(t);
//// t.equals(gt, oom, rm);
// return false;
// }
// }
// }
// for (V3D_Triangle t : i.triangles) {
// V3D_Geometry g = getIntersection(t, oom, rm);
// if (g instanceof V3D_Triangle gt) {
// if (!t.equals(gt, oom, rm)) {
// return false;
// }
// }
// }
// return true;
}
@Override
public V3D_Envelope getEnvelope(int oom, RoundingMode rm) {
if (en == null) {
en = points.get(0).getEnvelope(oom, rm);
for (int i = 1; i < points.size(); i++) {
en = en.union(points.get(i).getEnvelope(oom, rm), oom, rm);
}
}
return en;
}
/**
* If this is effectively a triangle, the triangle is returned. If this is
* effectively a rectangle, the rectangle is returned. Otherwise this is
* returned.
*
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return Either a triangle, rectangle or this.
*/
public V3D_FiniteGeometry simplify(int oom, RoundingMode rm) {
if (isTriangle()) {
return new V3D_Triangle(points.get(0), points.get(1),
points.get(2), oom, rm);
} else if (isRectangle(oom, rm)) {
return new V3D_Rectangle(points.get(0), points.get(2),
points.get(1), points.get(3), oom, rm);
} else {
return this;
}
}
/**
* Identify if this is intersected by point {@code p}.
*
* @param pt The point to test for intersection with.
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode if rounding is needed.
* @return {@code true} iff the geometry is intersected by {@code p}.
*/
public boolean isIntersectedBy(V3D_Point pt, int oom, RoundingMode rm) {
if (getEnvelope(oom, rm).isIntersectedBy(pt, oom, rm)) {
if (triangles.get(0).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 for any rounding.
* @return {@code true} if this intersects with {@code pt}.
*/
protected boolean isIntersectedBy0(V3D_Point pt, int oom, RoundingMode rm) {
for (V3D_Triangle triangle : triangles) {
if (triangle.isIntersectedBy0(pt, oom, rm)) {
return true;
}
}
return false;
//return triangles.parallelStream().anyMatch(t -> (t.isIntersectedBy0(pt, oom)));
}
// @Override
// public boolean isIntersectedBy(V3D_Line l, int oom, RoundingMode rm) {
// if (triangles.get(0).pl.isIntersectedBy(l, oom, rm)) {
// //return triangles.parallelStream().anyMatch(t -> (t.isIntersectedBy(l, oom)));
// return triangles.stream().anyMatch(t -> (t.isIntersectedBy(l, oom, rm)));
// }
// return false;
// }
//
// @Override
// public boolean isIntersectedBy(V3D_LineSegment l, int oom, RoundingMode rm) {
// if (getEnvelope(oom, rm).isIntersectedBy(l.getEnvelope(oom, rm), oom, rm)) {
// if (triangles.get(0).pl.isIntersectedBy(l, oom, rm)) {
// for (V3D_Triangle triangle : triangles) {
// if (triangle.isIntersectedBy(l, oom, rm)) {
// return true;
// }
// }
// return false;
// //return triangles.parallelStream().anyMatch(t -> (t.isIntersectedBy(l, oom, b)));
// }
// }
// return false;
// }
/**
* This sums all the areas irrespective of any overlaps.
*
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return The area of the triangle (rounded).
*/
@Override
public BigDecimal getArea(int oom, RoundingMode rm) {
BigDecimal sum = BigDecimal.ZERO;
for (var t : triangles) {
sum = sum.add(t.getArea(oom, rm));
}
return sum;
}
/**
* This sums all the perimeters irrespective of any overlaps.
*
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
*/
@Override
public BigDecimal getPerimeter(int oom, RoundingMode rm) {
BigDecimal sum = BigDecimal.ZERO;
for (var t : triangles) {
sum = sum.add(t.getPerimeter(oom, rm));
}
return sum;
}
/**
* Get the intersection between this and the plane {@code p}.
*
* @param p The plane 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_Plane p, int oom, RoundingMode rm) {
if (this.triangles.get(0).pl.equalsIgnoreOrientation(p, oom, rm)) {
return this;
}
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
/**
* Get the intersection between the geometry and the triangle {@code t}.
*
* @param t The triangle 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_Triangle t, int oom, RoundingMode rm) {
// Create a set all the intersecting triangles from this.
List ts = new ArrayList<>();
for (V3D_Triangle t2 : triangles) {
V3D_FiniteGeometry i = t2.getIntersection(t, oom, rm);
ts.addAll(Arrays.asList(i.getPoints(oom, rm)));
}
ArrayList tsu = V3D_Point.getUnique(ts, oom, rm);
if (tsu.isEmpty()) {
return null;
} else {
return new V3D_ConvexHullCoplanar(oom, rm, t.pl.n,
tsu.toArray(V3D_Point[]::new)).simplify(oom, rm);
}
// switch (size) {
// case 0:
// return null;
// case 1:
// return t2s.iterator().next();
// case 2:
// Iterator ite = t2s.iterator();
// return getGeometry(ite.next(), ite.next(), oom);
// default:
// return getGeometry(oom, t2s.toArray(V3D_Triangle[]::new));
}
// @Override
// public boolean isEnvelopeIntersectedBy(V3D_Line l, int oom) {
// return getEnvelope().isIntersectedBy(l, oom);
// }
@Override
public V3D_ConvexHullCoplanar rotate(V3D_Line axis, Math_BigRational theta,
int oom, RoundingMode rm) {
V3D_Triangle[] rts = new V3D_Triangle[triangles.size()];
for (int i = 0; i < triangles.size(); i++) {
rts[0] = triangles.get(i).rotate(axis, theta, oom, rm);
}
return new V3D_ConvexHullCoplanar(oom, rm, rts);
}
/**
*
* @param pts
* @param p0
* @param p1
* @param n
* @param index
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return the number of points added.
*/
private void getConvexHull0(ArrayList pts, V3D_Point p0,
V3D_Point p1, V3D_Vector n, int index, int oom, RoundingMode rm) {
V3D_Plane pl = new V3D_Plane(p0, p1, new V3D_Point(
offset, p0.rel.add(n, oom, rm)), oom, rm);
AboveAndBelow ab = new AboveAndBelow(pts, pl, oom, rm);
// Process ab.a
{
if (!ab.a.isEmpty()) {
V3D_Point apt = ab.a.get(ab.maxaIndex);
points.add(index, apt);
index++;
V3D_Triangle atr = new V3D_Triangle(p0, p1, apt, oom, rm);
TreeSet removeIndexes = new TreeSet<>();
for (int i = 0; i < ab.a.size(); i++) {
if (atr.isIntersectedBy(ab.a.get(i), oom, rm)) {
removeIndexes.add(i);
}
}
Iterator ite = removeIndexes.descendingIterator();
while (ite.hasNext()) {
ab.a.remove(ite.next().intValue());
}
if (!ab.a.isEmpty()) {
// Divide again
V3D_Line l = new V3D_Line(p0, p1, oom, rm);
V3D_Point proj = l.getPointOfIntersection(apt, oom, rm);
getConvexHull0(ab.a, apt, proj, n, index, oom, rm);
}
}
}
index++;
// Process ab.b
{
if (!ab.b.isEmpty()) {
V3D_Point bpt = ab.b.get(ab.maxbIndex);
points.add(index, bpt);
V3D_Triangle btr = new V3D_Triangle(p0, p1, bpt, oom, rm);
TreeSet removeIndexes = new TreeSet<>();
for (int i = 0; i < ab.b.size(); i++) {
if (btr.isIntersectedBy(ab.b.get(i), oom, rm)) {
removeIndexes.add(i);
}
}
Iterator ite = removeIndexes.descendingIterator();
while (ite.hasNext()) {
ab.b.remove(ite.next().intValue());
}
if (!ab.b.isEmpty()) {
// Divide again
V3D_Line l = new V3D_Line(p0, p1, oom, rm);
V3D_Point proj = l.getPointOfIntersection(bpt, oom, rm);
getConvexHull0(ab.b, bpt, proj, n, index, oom, rm);
}
}
}
}
/**
* A class for helping to calculate a convex hull.
*/
public class AboveAndBelow {
/**
* The points that are above.
*/
public ArrayList a;
/**
* For storing the index of the point in {@link #a} that is the maximum.
*/
int maxaIndex;
/**
* The points that are below.
*/
public ArrayList b;
/**
* For storing the index of the point in {@link #b} that is the maximum.
*/
int maxbIndex;
/**
* Create a new instance.
*
* @param pts The points.
* @param pl The plane.
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
*/
public AboveAndBelow(ArrayList pts, V3D_Plane pl, int oom, RoundingMode rm) {
a = new ArrayList<>();
b = new ArrayList<>();
V3D_Vector n = pl.n;
Math_BigRational maxads = Math_BigRational.ZERO;
Math_BigRational maxbds = Math_BigRational.ZERO;
for (int i = 0; i < pts.size(); i++) {
V3D_Point pt = pts.get(i);
Math_BigRational t = pl.getPV().subtract(pt.rel, oom, rm).getDotProduct(n, oom, rm);
Math_BigRational ds = pl.getDistanceSquared(pts.get(i), oom, rm);
//System.out.println(pt.toString() + " " + t);
switch (t.compareTo(Math_BigRational.ZERO)) {
case 1 -> {
if (ds.compareTo(maxads) == 1) {
maxads = ds;
maxaIndex = a.size();
}
a.add(pt);
//System.out.println("Above the plane.");
}
case -1 -> {
if (ds.compareTo(maxbds) == 1) {
maxbds = ds;
maxbIndex = b.size();
}
b.add(pt);
//System.out.println("Below the plane.");
}
default -> {
//System.out.println("On the plane.");
}
}
}
}
}
/**
* If all {@link #triangles} form a single triangle return true
*
* @return {@code true} iff this is a triangle.
*/
public final boolean isTriangle() {
return points.size() == 3;
}
/**
* If all {@link #triangles} form a single triangle return true
*
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return {@code true} iff this is a rectangle.
*/
public boolean isRectangle(int oom, RoundingMode rm) {
if (points.size() == 4) {
return V3D_Rectangle.isRectangle(points.get(0),
points.get(1), points.get(2), points.get(3), oom, rm);
}
return false;
}
/**
* Clips this using the pl and return the part that is on the same side as
* pl.
*
* @param pl The plane that clips.
* @param p 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 for any rounding.
* @return null, the whole or a part of this.
*/
public V3D_FiniteGeometry clip(V3D_Plane pl, V3D_Point p, int oom, RoundingMode rm) {
V3D_FiniteGeometry i = getIntersection(pl, oom, rm);
if (i == null) {
V3D_Point pp = this.triangles.get(0).pl.getP();
if (pl.isOnSameSide(pp, p, oom, rm)) {
return this;
} else {
return null;
}
} else if (i instanceof V3D_Point ip) {
if (pl.isOnSameSide(ip, p, oom, rm)) {
V3D_Point pp = this.triangles.get(0).pl.getP();
if (pl.isOnSameSide(pp, p, oom, rm)) {
return this;
} else {
return ip;
}
} else {
return null;
}
} else {
// i instanceof V3D_LineSegment
V3D_LineSegment il = (V3D_LineSegment) i;
ArrayList pts = new ArrayList<>();
for (V3D_Point pt : points) {
if (pl.isOnSameSide(pt, p, oom, rm)) {
pts.add(pt);
}
}
if (pts.isEmpty()) {
return il;
} else {
return new V3D_ConvexHullCoplanar(oom, rm,
this.triangles.get(0).pl.n,
pts.toArray(V3D_Point[]::new));
}
}
}
/**
* 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 for any rounding.
* @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 pp = new V3D_Point(tp.offset.add(n, oom, rm), tp.rel);
V3D_Plane ppl = new V3D_Plane(tp, tq, pp, oom, rm);
V3D_Point qp = new V3D_Point(tq.offset.add(n, oom, rm), tq.rel);
V3D_Plane qpl = new V3D_Plane(tq, tr, qp, oom, rm);
V3D_Point rp = new V3D_Point(tr.offset.add(n, oom, rm), tr.rel);
V3D_Plane rpl = new V3D_Plane(tr, tp, rp, oom, rm);
V3D_FiniteGeometry cppl = clip(ppl, tr, 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) {
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, tq, 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, tq, oom, rm);
} else {
V3D_ConvexHullCoplanar ccc = (V3D_ConvexHullCoplanar) cc;
return ccc.clip(rpl, pt, oom, rm);
}
}
}
/**
* If pts are all equal then a V3D_Point is returned. If two are different,
* then a V3D_LineSegment is returned. Three different, then a V3D_Triangle
* is returned. If four or more are different then a V3D_ConvexHullCoplanar
* is returned.
*
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @param pts The points.
* @return Either a V3D_Point, V3D_LineSegment, V3D_Triangle, or
* V3D_ConvexHullCoplanar.
*/
public static V3D_FiniteGeometry getGeometry(int oom, RoundingMode rm, V3D_Point... pts) {
Set s = new HashSet<>();
s.addAll(Arrays.asList(pts));
Iterator i = s.iterator();
switch (s.size()) {
case 1 -> {
return i.next();
}
case 2 -> {
return new V3D_LineSegment(i.next(), i.next(), oom, rm);
}
case 3 -> {
return new V3D_Triangle(i.next(), i.next(), i.next(), oom, rm);
}
default -> {
V3D_Point ip = i.next();
V3D_Point iq = i.next();
V3D_Point ir = i.next();
while (V3D_Line.isCollinear(oom, rm, ip, iq, ir) && i.hasNext()) {
ir = i.next();
}
V3D_Plane pl;
if (V3D_Line.isCollinear(oom, rm, ip, iq, ir) && i.hasNext()) {
return new V3D_LineSegment(oom, rm, pts);
} else {
pl = new V3D_Plane(ip, iq, ir, oom, rm);
return new V3D_ConvexHullCoplanar(oom, rm, pl.n, pts);
}
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy