Commit 69a6b798 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen
Browse files

refactoring of the triangulation code

parent 5bf617f9
......@@ -7,6 +7,7 @@ import org.vadere.util.geometry.data.DAG;
import org.vadere.util.geometry.data.Face;
import org.vadere.util.geometry.data.HalfEdge;
import org.vadere.util.geometry.GeometryUtils;
import org.vadere.util.geometry.data.Triangulation;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VCircle;
import org.vadere.util.geometry.shapes.VLine;
......@@ -24,12 +25,13 @@ import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.*;
public class BowyerWatson<P extends IPoint> {
public class BowyerWatson<P extends IPoint> implements Triangulation<P> {
private final Collection<P> points;
private final Set<P> points;
private final PointConstructor<P> pointConstructor;
private final TriangleConstructor<VPoint> triangleConstructor;
private P p0;
......@@ -37,17 +39,17 @@ public class BowyerWatson<P extends IPoint> {
private P p2;
private DAG<DAGElement<P>> dag;
private final HashMap<Face<P>, DAG<DAGElement<P>>> map;
private Set<DAG<DAGElement<P>>> triangles;
private int count;
public BowyerWatson(
final Collection<P> points,
final Set<P> points,
final PointConstructor<P> pointConstructor,
final TriangleConstructor<VPoint> triangleConstructor) {
this.points = points;
this.pointConstructor = pointConstructor;
this.triangleConstructor = triangleConstructor;
this.map = new HashMap<>();
this.triangles = new HashSet<>();
this.count = 0;
}
public void init() {
......@@ -72,45 +74,86 @@ public class BowyerWatson<P extends IPoint> {
int count = 0;
// 2. insert points
for(P p : points) {
// find triangle containing p using the DAG.
Set<DAG<DAGElement<P>>> leafs = locatePoint(p);
assert leafs.size() == 2 ||leafs.size() == 1;
insert(p);
count++;
// point is inside a triangle
if(leafs.size() == 1) {
split(p, leafs.stream().findAny().get());
} // point lies on an edge of 2 triangles
else if(leafs.size() == 2) {
Iterator<DAG<DAGElement<P>>> it = leafs.iterator();
splitBoth(p, it.next(), it.next());
}
else if(leafs.size() == 0) {
// problem due numerical calculation.
System.out.println("numerical error!");
}
else {
Set<DAG<DAGElement<P>>> leafs2 = locatePoint(p);
System.out.println(leafs2 + " contains " + p);
throw new IllegalArgumentException("something is wrong here, this should never happen " + leafs.size() + " / " + p + " / " + count);
}
}
}
// 3. remove initial points
triangles = map.values().stream().filter(
dagElement -> {
@Override
public Face<P> locate(P point) {
return locatePoint(point).stream().findAny().get().getElement().getFace();
}
@Override
public Set<Face<P>> getFaces() {
return streamFaces().collect(Collectors.toSet());
}
@Override
public Stream<Face<P>> streamFaces() {
return map.values().stream()
.filter(dagElement -> {
Triple<P, P, P> triple = dagElement.getElement().getVertices();
Set<P> pointset = new HashSet<P>();
Set<P> pointset = new HashSet<>();
pointset.add(triple.getLeft());
pointset.add(triple.getMiddle());
pointset.add(triple.getRight());
return !pointset.contains(p0) && !pointset.contains(p1) && !pointset.contains(p2);
}
).collect(Collectors.toSet());
})
.map(dagElementDAG -> dagElementDAG.getElement().getFace());
}
@Override
public void insert(P point) {
Set<DAG<DAGElement<P>>> leafs = locatePoint(point);
assert leafs.size() == 2 ||leafs.size() == 1;
count++;
// point is inside a triangle
if(leafs.size() == 1) {
split(point, leafs.stream().findAny().get());
} // point lies on an edge of 2 triangles
else if(leafs.size() == 2) {
Iterator<DAG<DAGElement<P>>> it = leafs.iterator();
splitBoth(point, it.next(), it.next());
}
else if(leafs.size() == 0) {
// problem due numerical calculation.
System.out.println("numerical error!");
}
else {
Set<DAG<DAGElement<P>>> leafs2 = locatePoint(point);
System.out.println(leafs2 + " contains " + point);
throw new IllegalArgumentException("something is wrong here, this should never happen " + leafs.size() + " / " + point + " / " + count);
}
}
@Override
public void remove(P point) {
throw new UnsupportedOperationException("not jet implemented.");
}
public Collection<VTriangle> getTriangles() {
return getFaces().stream().map(face -> faceToTriangle(face)).collect(Collectors.toSet());
}
/*public Collection<Triple<P, P, P>> getTrianglePoints() {
return triangles.stream().map(dagElement -> dagElement.getElement().getVertices()).collect(Collectors.toList());
}*/
public Set<VLine> getEdges() {
return getTriangles().stream().flatMap(triangle -> triangle.getLineStream()).collect(Collectors.toSet());
}
private VTriangle faceToTriangle(final Face<P> face) {
List<P> points = face.getPoints();
P p1 = points.get(0);
P p2 = points.get(1);
P p3 = points.get(2);
return new VTriangle(new VPoint(p1.getX(), p1.getY()), new VPoint(p2.getX(), p2.getY()), new VPoint(p3.getX(), p3.getY()));
}
public Set<DAG<DAGElement<P>>> locatePoint(final P point) {
private Set<DAG<DAGElement<P>>> locatePoint(final P point) {
Set<DAG<DAGElement<P>>> leafs = new HashSet<>();
LinkedList<DAG<DAGElement<P>>> nodesToVisit = new LinkedList<>();
......@@ -154,19 +197,9 @@ public class BowyerWatson<P extends IPoint> {
return leafs;
}
public Collection<VTriangle> getTriangles() {
return triangles.stream().map(dagElement -> dagElement.getElement().getTriangle()).collect(Collectors.toList());
}
public Collection<Triple<P, P, P>> getTrianglePoints() {
return triangles.stream().map(dagElement -> dagElement.getElement().getVertices()).collect(Collectors.toList());
}
public Set<VLine> getEdges() {
return triangles.stream().flatMap(dagElement -> dagElement.getElement().getTriangle().getLineStream()).collect(Collectors.toSet());
}
public Pair<DAG<DAGElement<P>>, DAG<DAGElement<P>>> splitBoth(@NotNull P p, @NotNull DAG<DAGElement<P>> xyzDag, @NotNull DAG<DAGElement<P>> xwzDag) {
System.out.println("split both");
VTriangle xyzTriangle = xyzDag.getElement().getTriangle();
Face<P> xyzFace = xyzDag.getElement().getFace();
List<HalfEdge<P>> edges = xyzFace.getEdges();
......@@ -307,16 +340,16 @@ public class BowyerWatson<P extends IPoint> {
P y = xy.getEnd();
P z = yz.getEnd();
HalfEdge<P> yp = new HalfEdge<P>(p, xyp);
HalfEdge<P> py = new HalfEdge<P>(y, yzp);
HalfEdge<P> yp = new HalfEdge<>(p, xyp);
HalfEdge<P> py = new HalfEdge<>(y, yzp);
yp.setTwin(py);
HalfEdge<P> xp = new HalfEdge<P>(p, zxp);
HalfEdge<P> px = new HalfEdge<P>(x, xyp);
HalfEdge<P> xp = new HalfEdge<>(p, zxp);
HalfEdge<P> px = new HalfEdge<>(x, xyp);
xp.setTwin(px);
HalfEdge<P> zp = new HalfEdge<P>(p, yzp);
HalfEdge<P> pz = new HalfEdge<P>(z, zxp);
HalfEdge<P> zp = new HalfEdge<>(p, yzp);
HalfEdge<P> pz = new HalfEdge<>(z, zxp);
zp.setTwin(pz);
zx.setNext(xp);
......@@ -434,6 +467,10 @@ public class BowyerWatson<P extends IPoint> {
zy.setFace(f1);
yp.setFace(f1);
if(y.equals(p)) {
System.out.println("" + y + p);
}
// update DAG
DAG<DAGElement<P>> yzpDag = new DAG<>(new DAGElement(f1, Triple.of(y, z, p), triangleConstructor));
DAG<DAGElement<P>> xypDag = new DAG<>(new DAGElement(f2, Triple.of(x, y, p), triangleConstructor));
......@@ -452,30 +489,6 @@ public class BowyerWatson<P extends IPoint> {
}
}
/*public class DAGElement {
private Face<P> face;
private Triple<P, P, P> vertices;
private T triangle;
public DAGElement(final Face<P> face, final Triple<P, P, P> vertices) {
this.face = face;
this.vertices = vertices;
this.triangle = triangleConstructor.create(vertices.getLeft(), vertices.getMiddle(), vertices.getRight());
}
public Face<P> getFace() {
return face;
}
public T getTriangle() {
return triangle;
}
public Triple<P, P, P> getVertices() {
return vertices;
}
}*/
// TODO: the following code can be deleted, this is only for visual checks
public static void main(String[] args) {
// TODO Auto-generated method stub
......
package org.vadere.util.delaunay;
public class GuibasDAC {
}
......@@ -2,10 +2,11 @@ package org.vadere.util.geometry;
import java.util.Comparator;
import org.vadere.util.geometry.shapes.MPoint;
import org.vadere.util.geometry.shapes.VPoint;
/**
* Point class representing one {@link VPoint} in 2D space with an additional
* Point class representing one {@link MPoint} in 2D space with an additional
* double-valued data store.
*
* An order can be applied with the isGreaterThan method.
......@@ -13,30 +14,38 @@ import org.vadere.util.geometry.shapes.VPoint;
*/
public class DataPoint extends VPoint implements Comparable<VPoint> {
private static final long serialVersionUID = 4631007066694627415L;
private static Comparator<DataPoint> comparator = (d1, d2) -> {
if (Math.abs(d1.data - d2.data) < GeometryUtils.DOUBLE_EPS) {
return 0;// do not compare coordinates
} else if (d1.data < d2.data) {
return -1;
}
return 1;
};
private static Comparator<DataPoint> pointComparator = (d1, d2) -> {
if (Math.abs(d1.data - d2.data) < GeometryUtils.DOUBLE_EPS) {
// compare coordinates
return d1.compareTo(d2);
} else if (d1.data < d2.data) {
return -1;
}
return 1;
};
private double data;
public DataPoint(double x, double y, double data) {
public DataPoint(final double x, final double y, final double data) {
super(x, y);
this.data = data;
}
public DataPoint(double x, double y) {
public DataPoint(final double x, final double y) {
this(x, y, 0);
}
public DataPoint(VPoint p, double data) {
this(p.x, p.y, data);
}
public DataPoint(VPoint p1) {
super(p1.x, p1.y);
if (p1.getClass().equals(DataPoint.class)) {
this.data = ((DataPoint) p1).data;
} else {
this.data = 0.0;
}
public DataPoint(final VPoint p, final double data) {
this(p.getX(), p.getY(), data);
}
/**
......@@ -50,13 +59,13 @@ public class DataPoint extends VPoint implements Comparable<VPoint> {
* @param data
* the data to set
*/
public void setData(double data) {
public void setData(final double data) {
this.data = data;
}
@Override
public String toString() {
return "(" + this.x + "," + this.y + "&" + this.data + ")";
return "(" + this.getX() + "," + this.getY() + "&" + this.data + ")";
}
/**
......@@ -64,19 +73,8 @@ public class DataPoint extends VPoint implements Comparable<VPoint> {
*
* @return
*/
public static Comparator<DataPoint> getComparator() {
return new Comparator<DataPoint>() {
@Override
public int compare(DataPoint d1, DataPoint d2) {
if (Math.abs(d1.data - d2.data) < GeometryUtils.DOUBLE_EPS) {
return 0;// do not compare coordinates
} else if (d1.data < d2.data) {
return -1;
}
return 1;
}
};
public static Comparator<? super DataPoint> getComparator() {
return comparator;
}
/**
......@@ -86,35 +84,24 @@ public class DataPoint extends VPoint implements Comparable<VPoint> {
* @return
*/
public static Comparator<? super DataPoint> getPointComparator() {
return new Comparator<DataPoint>() {
@Override
public int compare(DataPoint d1, DataPoint d2) {
if (Math.abs(d1.data - d2.data) < GeometryUtils.DOUBLE_EPS) {
// compare coordinates
return d1.compareTo(d2);
} else if (d1.data < d2.data) {
return -1;
}
return 1;
}
};
return pointComparator;
}
/**
* Compares the points without using the data value of this point.
*/
@Override
public int compareTo(VPoint other) {
if (Math.abs(this.x - other.x) < GeometryUtils.DOUBLE_EPS) {
if (Math.abs(this.y - other.y) < GeometryUtils.DOUBLE_EPS) {
public int compareTo(final VPoint other) {
if (Math.abs(getX() - other.getX()) < GeometryUtils.DOUBLE_EPS) {
if (Math.abs(getY() - other.getY()) < GeometryUtils.DOUBLE_EPS) {
return 0;
} else {
if (this.y > other.y) {
if (getY() > other.getY()) {
return 1;
}
}
} else {
if (this.x > other.x) {
if (this.getX() > other.getX()) {
return 1;
} else {
return -1;
......
package org.vadere.util.geometry;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VLine;
import org.vadere.util.geometry.shapes.VPoint;
import java.awt.geom.Line2D;
import java.util.Iterator;
public class LineIterator implements Iterator<IPoint> {
private final Line2D.Double line;
private final double delta;
private VPoint startPoint;
private VPoint currentPoint;
private VPoint endPoint;
private VPoint deltaPoint;
private double slope;
private double lineLength;
private double dx;
private double dy;
private int counter;
private int numberOfSegments;
public LineIterator(final Line2D.Double line, final double delta) {
this.line = line;
if(line.getX1() < line.getX2()) {
startPoint = new VPoint(line.getX1(), line.getY1());
endPoint = new VPoint(line.getX2(), line.getY2());
}
else if(line.getX1() == line.getX2()) {
if(line.getY1() < line.getY2()) {
startPoint = new VPoint(line.getX1(), line.getY1());
endPoint = new VPoint(line.getX2(), line.getY2());
}
else if(line.getY1() > line.getY2()) {
startPoint = new VPoint(line.getX2(), line.getY2());
endPoint = new VPoint(line.getX1(), line.getY1());
}
else {
throw new IllegalArgumentException(line + " is not a feasible line.");
}
}
else {
startPoint = new VPoint(line.getX2(), line.getY2());
endPoint = new VPoint(line.getX1(), line.getY1());
}
lineLength = startPoint.distance(endPoint);
numberOfSegments = (int)Math.floor(lineLength / delta) - 3;
this.delta = lineLength / numberOfSegments;
if(line.getX1() == line.getX2()) {
dx = 0;
dy = this.delta;
slope = 0;
}
if(line.getY1() == line.getY2()) {
dx = this.delta;
dy = 0;
slope = 0;
}
if(line.getX1() != line.getX2() && line.getY1() != line.getY2()) {
double len = startPoint.distance(endPoint);
slope = new VLine(startPoint, endPoint).slope();
dx = Math.sqrt((this.delta * this.delta) / (1 + slope*slope));
dy = dx * slope;
}
deltaPoint = new VPoint(dx, dy);
currentPoint = null;
}
@Override
public boolean hasNext() {
return currentPoint == null || !currentPoint.equals(endPoint);
}
@Override
public IPoint next() {
// first point
if(currentPoint == null) {
counter++;
currentPoint = startPoint;
}
else if(counter < numberOfSegments) {
counter++;
currentPoint = currentPoint.add(deltaPoint);
} // last point
else {
currentPoint = endPoint;
}
if(slope != 0) {
// this is more accurate for slope != 0.
return new VPoint(currentPoint.getX(), startPoint.getY() + slope * (currentPoint.getX() - startPoint.getX()));
}
else {
return currentPoint;
}
}
}
......@@ -3,11 +3,16 @@ package org.vadere.util.geometry.data;
import org.jetbrains.annotations.NotNull;
import org.vadere.util.geometry.GeometryUtils;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.MLine;
import org.vadere.util.geometry.shapes.VLine;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VPolygon;
import org.vadere.util.geometry.shapes.VTriangle;
import java.awt.geom.Path2D;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
......@@ -19,6 +24,23 @@ import java.util.stream.StreamSupport;
*/
public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
public static <P extends IPoint> Face<P> of(P p1, P p2, P p3) {
Face superTriangle = new Face();
HalfEdge edge1 = new HalfEdge(p1, superTriangle);
HalfEdge edge2 = new HalfEdge(p2, superTriangle);
HalfEdge edge3 = new HalfEdge(p3, superTriangle);
edge1.setNext(edge2);
edge2.setNext(edge3);
edge3.setNext(edge1);
superTriangle.setEdge(edge1);
return superTriangle;
}
public static <P extends IPoint> Face<P> getBorder(Class<P> p) {
return new Face<>();
}
/**
* One of the half-edges bordering this face.
*/
......@@ -40,18 +62,6 @@ public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
*/
public Face() {}
public static <P extends IPoint> Face<P> of(P p1, P p2, P p3) {
Face superTriangle = new Face();
HalfEdge edge1 = new HalfEdge(p1, superTriangle);
HalfEdge edge2 = new HalfEdge(p2, superTriangle);
HalfEdge edge3 = new HalfEdge(p3, superTriangle);
edge1.setNext(edge2);
edge2.setNext(edge3);
edge3.setNext(edge1);
superTriangle.setEdge(edge1);
return superTriangle;
}
/**
* Sets one of the half-edges bordering this face.
* @param edge half-edge bordering this face
......@@ -60,6 +70,10 @@ public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
this.edge = edge;
}
public HalfEdge<P> getEdge() {
return edge;
}
/**
* Computes the area of this face.
* @return the area of this face
......@@ -89,6 +103,19 @@ public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
return new VPolygon(path2D);
}
public VTriangle toTriangle() {
List<HalfEdge<P>> edges = getEdges();
if(edges.size() != 3) {
throw new IllegalArgumentException("this face is not a feasible triangle.");