Commit 4f7049dd authored by Benedikt Zoennchen's avatar Benedikt Zoennchen

New version of the Distmeshalgorithm finally works

parent 8d9e9bef
package org.vadere.util.delaunay;
import org.vadere.util.geometry.shapes.VCircle;
import org.vadere.util.geometry.shapes.VLine;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VTriangle;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.swing.*;
/**
* @author Benedikt Zoennchen
*
* This class is for computing the DelaunayTriangulation using the BowyerWatson-Algorithm. In average the algorithm should perfom in O(n LOG(n)) but
* in degenerated cases its runtime can be in O(n^2) where n is the number of points.
*/
public class BowyerWatson3 {
private List<VTriangle> triangles;
private Collection<VPoint> points;
private List<VPoint> initPoints;
public BowyerWatson3(final Collection<VPoint> points) {
this.points = points;
}
public void execute() {
VPoint max = points.parallelStream().reduce(new VPoint(Integer.MIN_VALUE,Integer.MIN_VALUE), (a, b) -> new VPoint(Math.max(a.getX(), b.getX()), Math.max(a.getY(), b.getY())));
VPoint min = points.parallelStream().reduce(new VPoint(Integer.MAX_VALUE,Integer.MAX_VALUE), (a, b) -> new VPoint(Math.min(a.getX(), b.getX()), Math.min(a.getY(), b.getY())));
VRectangle bound = new VRectangle(min.getX(), min.getY(), max.getX()-min.getX(), max.getY()- min.getY());
init(bound);
points.stream().forEach(point -> handle(point));
cleanUp();
}
public List<VTriangle> getTriangles() {
return triangles;
}
public Set<VLine> getEdges() {
return triangles.parallelStream().flatMap(triangle -> Stream.of(triangle.getLines())).collect(Collectors.toSet());
}
private void init(final VRectangle bound) {
triangles = new ArrayList<>();
initPoints = new ArrayList<>();
VTriangle superTriangle = getSuperTriangle(bound);
triangles.add(superTriangle);
initPoints.addAll(superTriangle.getPoints());
}
private VTriangle getSuperTriangle(final VRectangle bound) {
double gap = 1.0;
double max = Math.max(bound.getWidth(), bound.getHeight());
VPoint p1 = new VPoint(bound.getX() - max - gap, bound.getY() - gap);
VPoint p2 = new VPoint(bound.getX() + 2 * max + gap, bound.getY() - gap);
VPoint p3 = new VPoint(bound.getX() + (max+2*gap)/2, bound.getY() + 2 * max+ gap);
return new VTriangle(p1, p2, p3);
}
private void handle(final VPoint point) {
HashSet<VLine> edges = new HashSet<>();
Map<Boolean, List<VTriangle>> partition = triangles.parallelStream().collect(Collectors.partitioningBy(triangle -> triangle.isInCircumscribedCycle(point)));
List<VTriangle> badTriangles = partition.get(true);
triangles = partition.get(false);
IntStream s;
HashSet<VLine> toRemove = new HashSet<>();
// duplicated edges
badTriangles.stream().flatMap(tri -> Stream.of(tri.getLines())).forEach(line -> {
if(!edges.add(line)) {
toRemove.add(line);
}
});
toRemove.stream().forEach(removeEdge -> edges.remove(removeEdge));
edges.stream().forEach(edge -> {
String[] id = edge.getIdentifier().split(":");
VPoint p1 = new VPoint(edge.getP1().getX(), edge.getP1().getY(), Integer.parseInt(id[0]));
VPoint p2 = new VPoint(edge.getP2().getX(), edge.getP2().getY(), Integer.parseInt(id[1]));
triangles.add(new VTriangle(p1, p2, point));
});
}
private void cleanUp() {
triangles = triangles.stream().filter(triangle -> !isTriangleConnectedToInitialPoints(triangle)).collect(Collectors.toList());
}
private boolean isTriangleConnectedToInitialPoints(final VTriangle triangle) {
return Stream.of(triangle.getLines()).anyMatch(edge -> {
VPoint p1 = new VPoint(edge.getP1().getX(), edge.getP1().getY());
VPoint p2 = new VPoint(edge.getP2().getX(), edge.getP2().getY());
return initPoints.stream().anyMatch(initPoint -> p1.equals(initPoint) || p2.equals(initPoint));
});
}
// TODO: the following code can be deleted, this is only for visual checks
public static void main(String[] args) {
// TODO Auto-generated method stub
int height = 1000;
int width = 1000;
int max = Math.max(height, width);
Set<VPoint> points = new HashSet<>();
/*points.add(new VPoint(20,20));
points.add(new VPoint(20,40));
points.add(new VPoint(75,53));
points.add(new VPoint(80,70));*/
Random r = new Random();
for(int i=0; i<10000; i++) {
VPoint point = new VPoint(width*r.nextDouble(), height*r.nextDouble());
points.add(point);
}
BowyerWatson3 bw = new BowyerWatson3(points);
bw.execute();
Set<VLine> edges = bw.getEdges();
JFrame window = new JFrame();
window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
window.setBounds(0, 0, max, max);
window.getContentPane().add(new Lines(edges, points, max));
window.setVisible(true);
}
private static class Lines extends JComponent{
private Set<VLine> edges;
private Set<VPoint> points;
private final int max;
public Lines(final Set<VLine> edges, final Set<VPoint> points, final int max){
this.edges = edges;
this.points = points;
this.max = max;
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setBackground(Color.white);
g2.setStroke(new BasicStroke(1.0f));
g2.setColor(Color.gray);
edges.stream().forEach(edge -> {
Shape k = new VLine(edge.getP1().getX(), edge.getP1().getY(), edge.getP2().getX(), edge.getP2().getY());
g2.draw(k);
});
points.stream().forEach(point -> {
VCircle k = new VCircle(point.getX(), point.getY(), 1.0);
g2.draw(k);
});
}
}
public void setTriangles(List<VTriangle> triangles) {
this.triangles = triangles;
}
}
......@@ -2,6 +2,7 @@ package org.vadere.util.delaunay;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Triple;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VCircle;
import org.vadere.util.geometry.shapes.VLine;
import org.vadere.util.geometry.shapes.VPoint;
......@@ -24,7 +25,7 @@ import java.util.stream.Stream;
* This class is for computing the DelaunayTriangulation using the BowyerWatson-Algorithm. In average the algorithm should perfom in O(n log(n)) but
* in degenerated cases its runtime can be in O(n^2) where n is the number of points.
*/
public class BowyerWatsonSlow<P extends VPoint> {
public class BowyerWatsonSlow<P extends IPoint> {
private List<Triple<P, P, P>> triangles;
private Collection<P> points;
private List<P> initPoints;
......@@ -36,24 +37,33 @@ public class BowyerWatsonSlow<P extends VPoint> {
}
public void execute() {
P max = points.parallelStream().reduce(pointConstructor.apply(Double.MIN_VALUE, Double.MIN_VALUE), (a, b) -> pointConstructor.apply(Math.max(a.getX(), b.getX()), Math.max(a.getY(), b.getY())));
P min = points.parallelStream().reduce(pointConstructor.apply(Double.MIN_VALUE, Double.MIN_VALUE), (a, b) -> pointConstructor.apply(Math.min(a.getX(), b.getX()), Math.min(a.getY(), b.getY())));
VRectangle bound = new VRectangle(min.getX(), min.getY(), max.getX()-min.getX(), max.getY()- min.getY());
// P max = points.parallelStream().reduce(pointConstructor.apply(Double.MIN_VALUE, Double.MIN_VALUE), (a, b) -> pointConstructor.apply(Math.max(a.getX(), b.getX()), Math.max(a.getY(), b.getY())));
// P min = points.parallelStream().reduce(pointConstructor.apply(Double.MAX_VALUE, Double.MAX_VALUE), (a, b) -> pointConstructor.apply(Math.min(a.getX(), b.getX()), Math.min(a.getY(), b.getY())));
P max = points.parallelStream().reduce(pointConstructor.apply(Double.MIN_VALUE,Double.MIN_VALUE), (a, b) -> pointConstructor.apply(Math.max(a.getX(), b.getX()), Math.max(a.getY(), b.getY())));
P min = points.parallelStream().reduce(pointConstructor.apply(Double.MAX_VALUE,Double.MAX_VALUE), (a, b) -> pointConstructor.apply(Math.min(a.getX(), b.getX()), Math.min(a.getY(), b.getY())));
VRectangle bound = new VRectangle(min.getX(), min.getY(), max.getX()-min.getX(), max.getY()- min.getY());
init(bound);
points.stream().forEach(point -> handle(point));
cleanUp();
}
public List<Triple<P, P, P>> getTriangles() {
/*public List<Triple<P, P, P>> getTriangles() {
return triangles;
}
}*/
public List<VTriangle> getVTriangles() {
/*public void setTriangles(List<VTriangle> triangles) {
this.triangles = triangles;
}*/
public List<VTriangle> getTriangles() {
return triangles.stream().map(this::pointsToTriangle).collect(Collectors.toList());
}
public Set<VLine> getEdges() {
return triangles.parallelStream().flatMap(triangle -> Stream.of(new VTriangle(triangle.getLeft(), triangle.getMiddle(), triangle.getRight()).getLines())).collect(Collectors.toSet());
return triangles.parallelStream().map(triple -> pointsToTriangle(triple)).flatMap(triangle -> triangle.getLineStream()).collect(Collectors.toSet());
}
private void init(final VRectangle bound) {
......@@ -113,6 +123,10 @@ public class BowyerWatsonSlow<P extends VPoint> {
triangles = triangles.stream().filter(triangle -> !isTriangleConnectedToInitialPoints(triangle)).collect(Collectors.toList());
}
public void removeTriangleIf(final Predicate<Triple<P, P, P>> predicate) {
triangles.removeIf(predicate);
}
private boolean isTriangleConnectedToInitialPoints(final Triple<P, P, P> trianglePoints) {
return Stream.of(pointsToTriangle(trianglePoints).getLines()).anyMatch(edge -> {
VPoint p1 = new VPoint(edge.getP1().getX(), edge.getP1().getY());
......@@ -122,7 +136,10 @@ public class BowyerWatsonSlow<P extends VPoint> {
}
private VTriangle pointsToTriangle(Triple<P, P, P> points) {
return new VTriangle(points.getLeft(), points.getMiddle(), points.getRight());
return new VTriangle(
new VPoint(points.getLeft().getX(), points.getLeft().getY()),
new VPoint(points.getMiddle().getX(), points.getMiddle().getY()),
new VPoint(points.getRight().getX(), points.getRight().getY()));
}
private class Line {
......@@ -216,8 +233,4 @@ public class BowyerWatsonSlow<P extends VPoint> {
}
}
public void removeTriangleIf(final Predicate<Triple<P, P, P>> predicate) {
triangles.removeIf(predicate);
}
}
package org.vadere.util.delaunay;
import org.apache.commons.lang3.tuple.Triple;
import org.vadere.util.geometry.data.Face;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VTriangle;
public class DAGElement<P extends VPoint, T extends VTriangle> {
public class DAGElement<P extends IPoint> {
private Face<P> face;
private Triple<P, P, P> vertices;
private T triangle;
private VTriangle triangle;
public DAGElement(final Face<P> face, final Triple<P, P, P> vertices, final TriangleConstructor<P, T> triangleConstructor) {
public DAGElement(final Face<P> face, final Triple<P, P, P> vertices, final TriangleConstructor<VPoint> triangleConstructor) {
this.face = face;
this.vertices = vertices;
this.triangle = triangleConstructor.create(vertices.getLeft(), vertices.getMiddle(), vertices.getRight());
VPoint p1 = new VPoint(vertices.getLeft().getX(), vertices.getLeft().getY());
VPoint p2 = new VPoint(vertices.getMiddle().getX(), vertices.getMiddle().getY());
VPoint p3 = new VPoint(vertices.getRight().getX(), vertices.getRight().getY());
this.triangle = triangleConstructor.create(p1, p2, p3);
}
public Face<P> getFace() {
return face;
}
public T getTriangle() {
public VTriangle getTriangle() {
return triangle;
}
public Triple<P, P, P> getVertices() {
return vertices;
}
@Override
public String toString() {
return triangle.toString();
}
}
package org.vadere.util.delaunay;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.IPoint;
@FunctionalInterface
public interface PointConstructor<P extends VPoint> {
public interface PointConstructor<P extends IPoint> {
P create(double x, double y);
}
package org.vadere.util.delaunay;
import com.sun.javafx.UnmodifiableArrayList;
import org.vadere.util.geometry.data.Face;
import org.vadere.util.geometry.data.HalfEdge;
import org.vadere.util.geometry.shapes.VLine;
import org.vadere.util.geometry.shapes.VPoint;
......
package org.vadere.util.delaunay;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VTriangle;
@FunctionalInterface
public interface TriangleConstructor<P extends VPoint, T extends VTriangle> {
T create(P p1, P p2, P p3);
public interface TriangleConstructor<P extends IPoint> {
VTriangle create(P p1, P p2, P p3);
}
......@@ -394,7 +394,7 @@ public class GeometryUtils {
return Math.abs(result) / 2.0;
}
public static double areaOfPolygon(final IPoint... vertices) {
public static double areaOfPolygon(final IPoint... vertices) {
double result = 0;
for (int i = 0; i < vertices.length - 1; i++) {
......@@ -445,40 +445,6 @@ public class GeometryUtils {
* @return all intersection poins of the line with the circle i.e. 1, 2 or 0 results.
*/
public static VPoint[] intersection(final VLine line, final VCircle circle) {
VCircle normedCircle = new VCircle(new VPoint(0,0), circle.getRadius());
Point2D p1 = line.getP1();
Point2D p2 = line.getP2();
VLine normedLine = new VLine(new VPoint(p1.getX(), p1.getY()).subtract(circle.getCenter()),
new VPoint(p2.getX(), p2.getY()).subtract(circle.getCenter()));
double dx = normedLine.getX2() - normedLine.getX1();
double dy = normedLine.getY2() - normedLine.getY1();
double drSquare = dx * dx + dy * dy;
double dr = Math.sqrt(drSquare);
double radius = normedCircle.getRadius();
double determinant = normedLine.getX1() * normedLine.getY2() - normedLine.getX2() * normedLine.getY1();
double discreminant = radius * radius * drSquare - determinant * determinant;
if(discreminant < 0) {
return new VPoint[0];
}
else if(discreminant == 0){
return new VPoint[]{
new VPoint(determinant * dy / drSquare, -determinant * dx / drSquare).add(circle.getCenter())
};
}
else {
double sign = dy < 0 ? -1 : 1;
double x1 = (determinant * dy + sign * dx * Math.sqrt(discreminant)) / drSquare;
double y1 = (-determinant * dx + Math.abs(dy) * Math.sqrt(discreminant)) / drSquare;
double x2 = (determinant * dy - sign * dx * Math.sqrt(discreminant)) / drSquare;
double y2 = (-determinant * dx - Math.abs(dy) * Math.sqrt(discreminant)) / drSquare;
return new VPoint[]{ new VPoint(x1, y1).add(circle.getCenter()), new VPoint(x2, y2).add(circle.getCenter())};
}
}
public static VPoint[] intersection2(final VLine line, final VCircle circle) {
double m = line.slope();
double d = line.getY1() - m * line.getX1();
double a = circle.getCenter().getX();
......@@ -522,7 +488,6 @@ public class GeometryUtils {
}
/**
<<<<<<< HEAD
*
* Computes the angle between the x-axis through the given Point "center" and this.
* Result is in interval (0,2*PI) according to standard math usage.
......@@ -542,8 +507,6 @@ public class GeometryUtils {
}
/**
=======
>>>>>>> much faster implementation of the BowyerWatson algorithm and a splitting algorithm for obscuse triangles
* Returns the angle between line1 and line2 in clock wise order (cw).
* @param line1
* @param line2
......
package org.vadere.util.data;
package org.vadere.util.geometry.data;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
/**
* Directed Acyclic graph. Each node of the DAG is a DAG itself.
*
* @param <E> type of the elements of the DAG.
*/
public class DAG<E> {
private final E root;
/**
* The element of this DAG-Node.
*/
private final E element;
/**
* The children of the DAG-Node.
*/
private final List<DAG<E>> children;
/**
* Default constructor.
* @param element
*/
public DAG(@NotNull final E element) {
root = element;
this.element = element;
children = new ArrayList<>();
}
/**
* Returns all child nodes of the DAG.
* @return all child nodes of the DAG
*/
public List<DAG<E>> getChildren() {
return children;
}
/**
* Adds a new element as child node to the DAG.
* @param child a new element
*/
public void addChild(E child) {
this.children.add(new DAG<E>(child));
}
/**
* Adds a new Node to as child node to the DAG.
* @param child a new child node
*/
public void addChild(DAG<E> child) {
this.children.add(child);
}
public E getRoot() {
return root;
/**
* Returns the element of the DAG.
* @return
*/
public E getElement() {
return element;
}
public Collection<E> collectLeafs() {
Collection<E> leafs = new ArrayList<E>();
Collection<E> leafs = new ArrayList<>();
LinkedList<DAG<E>> nodesToVisit = new LinkedList<>();
nodesToVisit.add(this);
while (!nodesToVisit.isEmpty()) {
DAG<E> currentNode = nodesToVisit.removeFirst();
DAG<E> currentNode = nodesToVisit.removeLast();
nodesToVisit.addAll(currentNode.children);
if(currentNode.isLeaf())
leafs.add(currentNode.getRoot());
leafs.add(currentNode.getElement());
}
return leafs;
}
/**
* Test whether this DAG-Node is a child or not.
* @return true if this node is a child node, false otherwise.
*/
public boolean isLeaf(){
return children.isEmpty();
}
/**
* Finds the first DAG-node element in a dept first fashion.
* @param test the predicate the element of the DAG-node has to fullfill.
* @param test the predicate the element of the DAG-node has to fulfill.
* @return
*/
public Optional<E> findFirstElement(final Predicate<E> test){
Optional<DAG<E>> optDag = findFirst(test);
if(optDag.isPresent()) {
return Optional.of(optDag.get().getRoot());
return Optional.of(optDag.get().getElement());
}
else {
return Optional.empty();
......@@ -71,27 +110,81 @@ public class DAG<E> {
/**
* Finds the first DAG-node in a dept first fashion.
* @param test the predicate the element of the DAG-node has to fullfill.
* @param test the predicate the element of the DAG-node has to fulfill.
* @return
*/
public Optional<DAG<E>> findFirst(final Predicate<E> test){
if(test.test(root)) {
return Optional.of(this);
LinkedList<DAG<E>> nodesToVisit = new LinkedList<>();
nodesToVisit.add(this);
while(!nodesToVisit.isEmpty()) {
DAG<E> currentNode = nodesToVisit.removeLast();
if(test.test(currentNode.getElement())) {
return Optional.of(currentNode);
}
nodesToVisit.addAll(currentNode.children);
}
return Optional.empty();
}
/**
* Returns the last node of a path of elements that satisfy the test.
* The path will be constructed in a dept first fashion, therefore there
* may exist other paths.
*
* @param test
* @return
*/
public Optional<DAG<E>> matchFirst(final Predicate<E> test) {
DAG<E> currentNode = this;
if(!test.test(currentNode.getElement())){
return Optional.empty();