uk.ac.leeds.ccg.v3d.geometry.V3D_LineSegmentsCollinear 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 2019 Andy Turner, University of Leeds.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package uk.ac.leeds.ccg.v3d.geometry;
import 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.TreeSet;
import uk.ac.leeds.ccg.math.number.Math_BigRational;
import uk.ac.leeds.ccg.math.number.Math_BigRationalSqrt;
/**
* For representing multiple collinear line segments.
*
* Things to do:
*
* - Find extremes.
*
*
* @author Andy Turner
* @version 1.0
*/
public class V3D_LineSegmentsCollinear extends V3D_FiniteGeometry {
private static final long serialVersionUID = 1L;
/**
* The collinear line segments.
*/
public final ArrayList lineSegments;
/**
* Create a new instance.
*
* @param lineSegments What {@code #lineSegments} is set to.
*/
public V3D_LineSegmentsCollinear(V3D_LineSegment... lineSegments) {
super();
this.lineSegments = new ArrayList<>();
this.lineSegments.addAll(Arrays.asList(lineSegments));
}
@Override
public V3D_Point[] getPoints(int oom, RoundingMode rm) {
int nl = lineSegments.size();
V3D_Point[] r = new V3D_Point[nl * 2];
for (int i = 0; i < nl; i++) {
V3D_LineSegment l = lineSegments.get(i);
r[i] = l.getP();
r[i + nl] = l.getQ();
}
return r;
}
@Override
public String toString() {
String s = this.getClass().getName() + "(";
Iterator ite = lineSegments.iterator();
if (!lineSegments.isEmpty()) {
s += ite.next().toString();
}
while (ite.hasNext()) {
s += ", " + ite.next().toString();
}
return s + ")";
}
/**
* @param l The line segment to test if it is the same as {@code this}.
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return {@code true} iff {@code l} is the same as {@code this}.
*/
public boolean equals(V3D_LineSegmentsCollinear l, int oom, RoundingMode rm) {
HashSet indexes = new HashSet<>();
for (var x : lineSegments) {
boolean found = false;
for (int i = 0; i < l.lineSegments.size(); i++) {
if (x.equals(l.lineSegments.get(i), oom, rm)) {
found = true;
indexes.add(i);
break;
}
}
if (!found) {
return false;
}
}
for (int i = 0; i < l.lineSegments.size(); i++) {
if (!indexes.contains(i)) {
boolean found = false;
for (var x : lineSegments) {
if (x.equals(l.lineSegments.get(i), oom, rm)) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
}
return true;
// if (!l.lineSegments.containsAll(lineSegments)) {
// return false;
// }
// return lineSegments.containsAll(l.lineSegments);
}
/**
* If p0 and p1 are the same, return the point. Otherwise return a line
* segment.
*
* @param p0 A point which may be the same as p1.
* @param p1 A point which may be the same as p0.
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return A V3D_Point if p0 and p1 are equal, otherwise return a
* V3D_LineSegment with end points p0 an p1.
*/
public static V3D_FiniteGeometry getGeometry(V3D_Point p0,
V3D_Point p1, int oom, RoundingMode rm) {
if (p0.equals(p1, oom, rm)) {
return p0;
} else {
return new V3D_LineSegment(p0, p1, oom, rm);
}
}
/**
* @param l1 A line segment collinear with {@code l2}.
* @param l2 A line segment collinear with {@code l1}.
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return A V3D_LineSegmentsCollinear if {@code l1} and {@code l2} do not
* intersect, otherwise a single V3D_LineSegment.
*/
public static V3D_FiniteGeometry getGeometry(V3D_LineSegment l1,
V3D_LineSegment l2, int oom, RoundingMode rm) {
if (l1.getIntersection(l2, oom, rm) == null) {
return new V3D_LineSegmentsCollinear(l1, l2);
}
/**
* Check the type of intersection. {@code
* 1) l1p ---------- l1q
* l2p ---------- l2q
* 2) l1p ------------------------ l1q
* l2p ---------- l2q
* 3) l1p ---------- l1q
* l2p ------------------------ l2q
* 4) l1p ---------- l1q
* l2p ---------- l2q
* 5) l1q ---------- l1p
* l2p ---------- l2q
* 6) l1q ------------------------ l1p
* l2p ---------- l2q
* 7) l1q ---------- l1p
* l2p ------------------------ l2q
* 8) l1q ---------- l1p
* l2p ---------- l2q
* 9) l1p ---------- l1q
* l2q ---------- l2p
* 10) l1p ------------------------ l1q
* l2q ---------- l2p
* 11) l1p ---------- l1q
* l2q ------------------------ l2p
* 12) l1p ---------- l1q
* l2q ---------- l2p
* 13) l1q ---------- l1p
* l2q ---------- l2p
* 14) l1q ------------------------ l1p
* l2q ---------- l2p
* 15) l1q ---------- l1p
* l2q ------------------------ l2p
* 16) l1q ---------- l1p
* l2q ---------- l2p
* }
*/
V3D_Point l1p = l1.getP();
V3D_Point l1q = l1.getQ();
if (l2.isIntersectedBy(l1p, oom, rm)) {
// Cases 1, 2, 5, 6, 14, 16
if (l2.isIntersectedBy(l1q, oom, rm)) {
// Cases 2, 6, 14
/**
* The line segments are effectively the same although the start
* and end points may be opposite.
*/
//return l1;
return l2;
} else {
V3D_Point l2p = l2.getP();
// Cases 1, 5, 16
if (l1.isIntersectedBy(l2p, oom, rm)) {
// Cases 5
return getGeometry(l1p, l1q, oom, rm);
} else {
// Cases 1, 16
return getGeometry(l2p, l1q, oom, rm);
}
}
} else {
// Cases 3, 4, 7, 8, 9, 10, 11, 12, 13, 15
if (l2.isIntersectedBy(l1q, oom, rm)) {
V3D_Point l2p = l2.getP();
// Cases 4, 8, 9, 10, 11
if (l1.isIntersectedBy(l2p, oom, rm)) {
// Cases 4, 11, 13
if (l1.isIntersectedBy(l2.getQ(), oom, rm)) {
// Cases 11
return l2;
} else {
// Cases 4, 13
return getGeometry(l1p, l2.getQ(), oom, rm);
}
} else {
// Cases 8, 9, 10
V3D_Point tq = l2.getQ();
if (l1.isIntersectedBy(tq, oom, rm)) {
// Cases 8, 9
return getGeometry(l2.getQ(), l1q, oom, rm);
} else {
// Cases 10
return l1;
}
}
} else {
// Cases 3, 7, 12, 15
V3D_Point tp = l2.getP();
if (l1.isIntersectedBy(tp, oom, rm)) {
// Cases 3, 12, 15
V3D_Point tq = l2.getQ();
if (l1.isIntersectedBy(tq, oom, rm)) {
// Cases 3, 15
//return l2;
return l1;
} else {
// Cases 12
return getGeometry(l2.getP(), l1p, oom, rm);
}
} else {
// Cases 7
return l2;
}
}
}
}
@Override
public V3D_Envelope getEnvelope(int oom, RoundingMode rm) {
if (en == null) {
Iterator ite = lineSegments.iterator();
en = ite.next().getEnvelope(oom, rm);
while (ite.hasNext()) {
en = en.union(ite.next().getEnvelope(oom, rm), oom, rm);
}
}
return en;
}
/**
* Get the minimum distance to {@code p}.
*
* @param p A point.
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @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 p}.
*
* @param p A point.
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return The distance squared to {@code p}.
*/
public Math_BigRational getDistanceSquared(V3D_Point p, int oom, RoundingMode rm) {
if (isIntersectedBy(p, oom, rm)) {
return Math_BigRational.ZERO;
}
Iterator ite = lineSegments.iterator();
Math_BigRational d = ite.next().getDistanceSquared(p, oom, rm);
while (ite.hasNext()) {
d = d.min(ite.next().getDistanceSquared(p, oom, rm));
}
return d;
}
/**
* Get the minimum distance to {@code l}.
*
* @param l A line.
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return The minimum distance squared 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 for any rounding.
* @return The minimum distance squared to {@code l}.
*/
public Math_BigRational getDistanceSquared(V3D_Line l, int oom, RoundingMode rm) {
// if (isIntersectedBy(l, oom, rm)) {
// return Math_BigRational.ZERO;
// }
Iterator ite = lineSegments.iterator();
Math_BigRational d = ite.next().getDistanceSquared(l, oom, rm);
while (ite.hasNext()) {
d = d.min(ite.next().getDistanceSquared(l, oom, rm));
}
return d;
}
/**
* 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 for any rounding.
* @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 for any rounding.
* @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;
}
Iterator ite = lineSegments.iterator();
Math_BigRational d = ite.next().getDistanceSquared(l, oom, rm);
while (ite.hasNext()) {
d = d.min(ite.next().getDistanceSquared(l, oom, rm));
}
return d;
}
/**
* Identify if this is intersected by point {@code pt}.
*
* @param pt The point to test for intersection with.
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return {@code true} iff the geometry is intersected by {@code pt}.
*/
public boolean isIntersectedBy(V3D_Point pt, int oom, RoundingMode rm) {
Iterator ite = lineSegments.iterator();
while (ite.hasNext()) {
if (ite.next().isIntersectedBy(pt, oom, rm)) {
return true;
}
}
return false;
}
/**
* Get the intersection between this and the line {@code l}.
*
* @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 The V3D_Geometry.
*/
public V3D_Geometry getIntersection(V3D_Line l, int oom, RoundingMode rm) {
Iterator ite = lineSegments.iterator();
V3D_Geometry g = ite.next().getIntersection(l, oom, rm);
if (g == null) {
while (ite.hasNext()) {
g = ite.next().getIntersection(l, oom, rm);
if (g instanceof V3D_Point) {
return g;
}
}
return null;
} else if (g instanceof V3D_Point) {
return g;
} else {
return this;
}
}
/**
* Get the intersection between this and the line segment {@code l}. This is
* complicated in that the result of the intersection could be comprised of
* points and line segments. Line segments have to have different start and
* end points.
*
* @param ls 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 ls, int oom, RoundingMode rm) {
if (lineSegments.get(0).l.isCollinear(ls.l, oom, rm)) {
ArrayList ps = new ArrayList<>();
ArrayList lse = new ArrayList<>();
Iterator ite = lineSegments.iterator();
while (ite.hasNext()) {
V3D_Geometry g = ite.next().getIntersection(ls, oom, rm);
if (g != null) {
if (g instanceof V3D_Point gp) {
ps.add(gp);
} else {
lse.add((V3D_LineSegment) g);
}
}
}
if (!ps.isEmpty()) {
// Need to handle cases where we have points.
throw new UnsupportedOperationException();
} else {
if (lse.size() == 1) {
return lse.get(0);
} else {
V3D_LineSegmentsCollinear r
= new V3D_LineSegmentsCollinear(
lse.toArray(V3D_LineSegment[]::new));
return r.simplify(oom, rm);
}
}
} else {
// Find the point of intersection if there is one.
Iterator ite = lineSegments.iterator();
while (ite.hasNext()) {
V3D_FiniteGeometry g = ite.next().getIntersection(ls, oom, rm);
if (g != null) {
return g;
}
}
// Return null if there is no point of intersection.
return null;
}
}
/**
* Combines overlapping line segments into single line segments. If there is
* only one line segment, then a V3D_LineSegment is returned, otherwise a
* V3D_LineSegmentsCollinear is returned.
*
* @param oom The Order of Magnitude for the precision.
* @param rm The RoundingMode for any rounding.
* @return Either a V3D_LineSegment or a V3D_LineSegmentsCollinear which is
* a simplified version of this with overlapping line segments replaced with
* a single line segment.
*/
public V3D_FiniteGeometry simplify(int oom, RoundingMode rm) {
ArrayList dummy = new ArrayList<>();
dummy.addAll(lineSegments);
ArrayList r = simplify0(dummy, 0, oom, rm);
if (r.size() == 1) {
return r.get(0);
} else {
return new V3D_LineSegmentsCollinear(r.toArray(V3D_LineSegment[]::new));
}
}
private ArrayList simplify0(
ArrayList ls, int i, int oom, RoundingMode rm) {
V3D_LineSegment l0 = ls.get(i);
ArrayList r = new ArrayList<>();
TreeSet removeIndexes = new TreeSet<>();
r.addAll(ls);
for (int j = i; j < ls.size(); j++) {
V3D_LineSegment l1 = ls.get(j);
if (l0.getIntersection(l1, oom, rm) != null) {
V3D_Point l0p = l0.getP();
if (l1.isIntersectedBy(l0p, oom, rm)) {
V3D_Point l0q = l0.getQ();
if (l1.isIntersectedBy(l0q, oom, rm)) {
// l0 is completely overlapped by l1
removeIndexes.add(i);
} else {
V3D_Point l1p = l1.getP();
V3D_Point l1q = l1.getQ();
if (l0.isIntersectedBy(l1p, oom, rm)) {
removeIndexes.add(i);
removeIndexes.add(j);
r.add(new V3D_LineSegment(l1q, l0q, oom, rm));
} else {
removeIndexes.add(i);
removeIndexes.add(j);
r.add(new V3D_LineSegment(l1q, l1p, oom, rm));
}
}
} else {
V3D_Point l0q = l0.getQ();
if (l1.isIntersectedBy(l0q, oom, rm)) {
V3D_Point l1p = l1.getP();
V3D_Point l1q = l1.getQ();
if (l0.isIntersectedBy(l1.getP(), oom, rm)) {
removeIndexes.add(i);
removeIndexes.add(j);
r.add(new V3D_LineSegment(l1q, l0p, oom, rm));
} else {
removeIndexes.add(i);
removeIndexes.add(j);
r.add(new V3D_LineSegment(l1p, l0p, oom, rm));
}
} else {
// l1 is completely overlapped by l0
removeIndexes.add(j);
}
}
}
}
Iterator ite = removeIndexes.descendingIterator();
while (ite.hasNext()) {
r.remove(ite.next().intValue());
}
if (i < r.size() - 1) {
r = simplify0(r, i + 1, oom, rm);
}
return r;
}
@Override
public void translate(V3D_Vector v, int oom, RoundingMode rm) {
super.translate(v, oom, rm);
if (en != null) {
en.translate(v, oom, rm);
}
for (int i = 0; i < lineSegments.size(); i++) {
lineSegments.get(i).translate(v, oom, rm);
}
}
@Override
public V3D_LineSegmentsCollinear rotate(V3D_Line axis,
Math_BigRational theta, int oom, RoundingMode rm) {
V3D_LineSegment[] rls = new V3D_LineSegment[lineSegments.size()];
for (int i = 0; i < lineSegments.size(); i++) {
rls[0] = lineSegments.get(i).rotate(axis, theta, oom, rm);
}
return new V3D_LineSegmentsCollinear(rls);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy