Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 2db821e0 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen
Browse files

add adpative triangulation implementation

parent 43e77c48
package org.vadere.util.delaunay;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
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 javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @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 BowyerWatson<P extends VPoint> {
private List<Triple<P, P, P>> triangles;
private Collection<P> points;
private List<P> initPoints;
private final BiFunction<Double, Double, P> pointConstructor;
public BowyerWatson(final Collection<P> points, final BiFunction<Double, Double, P> pointConstructor) {
this.points = points;
this.pointConstructor = pointConstructor;
}
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());
init(bound);
points.stream().forEach(point -> handle(point));
cleanUp();
}
public List<Triple<P, P, P>> getTriangles() {
return triangles;
}
public List<VTriangle> getVTriangles() {
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());
}
private void init(final VRectangle bound) {
triangles = new ArrayList<>();
initPoints = new ArrayList<>();
Triple<P, P, P> superTriangle = getSuperTriangle(bound);
triangles.add(superTriangle);
initPoints.add(superTriangle.getLeft());
initPoints.add(superTriangle.getMiddle());
initPoints.add(superTriangle.getRight());
}
private Triple<P, P, P> getSuperTriangle(final VRectangle bound) {
double gap = 1.0;
double max = Math.max(bound.getWidth(), bound.getHeight());
P p1 = pointConstructor.apply(bound.getX() - max - gap, bound.getY() - gap);
P p2 = pointConstructor.apply(bound.getX() + 2 * max + gap, bound.getY() - gap);
P p3 = pointConstructor.apply(bound.getX() + (max+2*gap)/2, bound.getY() + 2 * max+ gap);
return ImmutableTriple.of(p1, p2, p3);
}
private void handle(final P point) {
HashSet<Line> edges = new HashSet<>();
Map<Boolean, List<Triple<P, P, P>>> partition = triangles.parallelStream().collect(Collectors.partitioningBy(t -> pointsToTriangle(t).isInCircumscribedCycle(point)));
List<Triple<P, P, P>> badTriangles = partition.get(true);
triangles = partition.get(false);
IntStream s;
HashSet<Line> toRemove = new HashSet<>();
// duplicated edges
badTriangles.stream().flatMap(t -> getEdges(t).stream()).forEach(line -> {
if(!edges.add(line)) {
toRemove.add(line);
}
});
toRemove.stream().forEach(removeEdge -> edges.remove(removeEdge));
// identifier ?
edges.stream().forEach(edge -> triangles.add(Triple.of(edge.p1, edge.p2, point)));
}
private List<Line> getEdges(Triple<P, P, P> triangle) {
List<Line> list = new ArrayList<>();
list.add(new Line(triangle.getLeft(), triangle.getMiddle()));
list.add(new Line(triangle.getMiddle(), triangle.getRight()));
list.add(new Line(triangle.getRight(), triangle.getLeft()));
return list;
}
private void cleanUp() {
triangles = triangles.stream().filter(triangle -> !isTriangleConnectedToInitialPoints(triangle)).collect(Collectors.toList());
}
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());
VPoint p2 = new VPoint(edge.getP2().getX(), edge.getP2().getY());
return initPoints.stream().anyMatch(initPoint -> p1.equals(initPoint) || p2.equals(initPoint));
});
}
private VTriangle pointsToTriangle(Triple<P, P, P> points) {
return new VTriangle(points.getLeft(), points.getMiddle(), points.getRight());
}
private class Line {
final P p1;
final P p2;
private Line(P p1, P p2) {
this.p1 = p1;
this.p2 = p2;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Line line = (Line) o;
return (p1.equals(line.p1) && p2.equals(line.p2)) || (p2.equals(line.p1) && p1.equals(line.p2));
}
@Override
public int hashCode() {
return p1.hashCode() * p2.hashCode();
}
}
// 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);
}
BowyerWatson<VPoint> bw = new BowyerWatson<VPoint>(points, (x, y) -> new VPoint(x, y));
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 removeTriangleIf(final Predicate<Triple<P, P, P>> predicate) {
triangles.removeIf(predicate);
}
}
......@@ -171,6 +171,10 @@ public class VTriangle extends VPolygon {
return getCircumcenter().distance(point) < getCircumscribedRadius();
}
public boolean isInCircumscribedCycle(final VPoint point) {
return getCircumcenter().distance(point) < getCircumscribedRadius();
}
/**
* Computes the inward facing normal vector for the given points of the
* triangle.
......@@ -196,10 +200,6 @@ public class VTriangle extends VPolygon {
return this.p1.equals(point) || this.p2.equals(point) || this.p3.equals(point);
}
public VLine[] getLines() {
return lines;
}
public Stream<VLine> getLineStream() {
return Arrays.stream(getLines());
}
......@@ -215,4 +215,8 @@ public class VTriangle extends VPolygon {
public String toString() {
return p1 + "-" + p2 + "-" + p3;
}
public VLine[] getLines() {
return lines;
}
}
package org.vadere.util.triangulation.adaptive;
import org.apache.commons.lang3.tuple.Pair;
import org.vadere.util.geometry.shapes.VLine;
public class IndexedVLine extends VLine {
private final IndexedVPoint p1;
private final IndexedVPoint p2;
public IndexedVLine(IndexedVPoint p1, IndexedVPoint p2) {
super(p1, p2);
this.p1 = p1;
this.p2 = p2;
}
@Override
public boolean equals(Object obj) {
if(obj == null) return false;
if(obj == this) return true;
if(obj.getClass() != getClass()) return false;
IndexedVLine line = (IndexedVLine) obj;
return line.getP1().equals(getP1()) && line.getP2().equals(getP2()) || line.getP2().equals(getP1()) && line.getP1().equals(getP2());
}
@Override
public int hashCode() {
return p1.hashCode() * p2.hashCode();
}
public Pair<Integer, Integer> getId() {
return Pair.of(p1.getId(), p2.getId());
}
}
package org.vadere.util.triangulation.adaptive;
import org.vadere.util.geometry.shapes.VPoint;
/**
* Created by bzoennchen on 10.11.16.
*/
public class IndexedVPoint extends VPoint {
private int id;
public IndexedVPoint(final VPoint point, final int id){
this(point.getX(), point.getY(), id);
}
public IndexedVPoint(final double x, final double y, final int id){
super(x, y);
this.id = id;
}
public IndexedVPoint subtract(VPoint point) {
return new IndexedVPoint(super.subtract(point), id);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
......@@ -20,7 +20,7 @@ import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class PSDistmesh {
public class PSDistmesh {
private Set<MeshPoint> points = new HashSet<>();
private Set<MLine<MeshPoint>> lines = new HashSet<>();
private IncrementalTriangulation<MeshPoint, PHalfEdge<MeshPoint>, PFace<MeshPoint>> bowyerWatson;
......@@ -96,7 +96,7 @@ public class PSDistmesh {
}
public boolean hasMaximalSteps() {
return steps >= Parameters.MAX_NUMBER_OF_STEPS;
return steps >= 1000;
}
/**
......
......@@ -4,18 +4,17 @@ package org.vadere.util.triangulation.adaptive;
* Created by Matimati-ka on 27.09.2016.
*/
public class Parameters {
final static double TOL = .1;
final static double FSCALE = 1.0;
final static double DELTAT = 0.2;
public final static double h0 = 0.15;
public final static boolean uniform = false;
public final static String method = "Distmesh"; // "Distmesh" or "Density"
final static double qualityMeasurement = 0.95;
final static double MINIMUM = 0.25;
final static double DENSITYWEIGHT = 2;
final static int NPOINTS = 100000;
final static int SAMPLENUMBER = 10;
final static int SAMPLEDIVISION = 10;
static final int SEGMENTDIVISION = 0;
final static int MAX_NUMBER_OF_STEPS = 20;
final static double TOL = .1;
final static double FSCALE = 1.2;
final static double DELTAT = 0.2;
public final static double h0 = 0.15;
public final static boolean uniform = false;
public final static String method = "Density"; // "Distmesh" or "Density"
final static double qualityMeasurement = 0.875;
final static double MINIMUM = 0.25;
final static double DENSITYWEIGHT = 2;
final static int NPOINTS = 100000;
final static int SAMPLENUMBER = 10;
final static int SAMPLEDIVISION = 10;
static final int SEGMENTDIVISION = 0;
}
package org.vadere.util.triangulation.adaptive;
import org.vadere.util.triangulation.BowyerWatson3;
import org.apache.commons.lang3.tuple.Pair;
import org.vadere.util.delaunay.BowyerWatson;
import org.vadere.util.geometry.shapes.VLine;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
......@@ -18,466 +18,440 @@ import java.util.stream.IntStream;
import static java.lang.Math.pow;
public class PerssonStrangDistmesh {
private List<VPoint> points = new ArrayList<>();
private List<VPoint> oldPoints = new ArrayList<>();
private BowyerWatson3 triangulation;
private Function<VPoint, Double> fd;
private Function<VPoint, Double> fh;
// Parameters
private double h0;
private double geps;
private double deps;
/*
Konstruktor für den Algorithmus von Persson und Strang
*/
public PerssonStrangDistmesh(VRectangle box,
Collection<? extends VShape> obstacles,
double h0,
boolean uniform,
Function<VPoint, Double> density,
String method) {
this.h0 = h0;
this.geps = .001*h0;
this.deps = 1.4901e-8*h0;
double MAXDENSITY = calculateMaxDensity(density, box);
fd = v -> {
double value = box.distance(v);
for (VShape obstacle : obstacles) {
value = doDDiff(value, obstacle.distance(v));
}
return value;
};
if(uniform)
{
fh = v -> 1.0;
} else {
fh = v -> {
double result;
switch (method) {
case "Distmesh":
result = 0.15 - 0.2 * box.distance(v);
double last = -box.distance(v);
for (VShape obstacle : obstacles) {
if (Math.max(box.getWidth(), box.getHeight()) <= 10) {
result = doDUnion(result, 0.06 + 0.2 * obstacle.distance(v));
last += obstacle.distance(v);
} else {
result = doDUnion(result, 0.06 + 0.2 * obstacle.distance(v) * 10 / Math.max(box.getWidth(), box.getHeight()));
last += obstacle.distance(v) * 10 / Math.max(box.getWidth(), box.getHeight());
}
}
last /= obstacles.size();
result = doDUnion(result, last);
break;
case "Density":
result = 1 / (Parameters.MINIMUM + (density.apply(v) / MAXDENSITY) * Parameters.DENSITYWEIGHT);
break;
default:
throw new RuntimeException("Method not accepted");
}
return result;
};
}
generatePoints(box);
removePointsAndRejection();
addFixPointsOnBoundary(new ArrayList<VShape>() {{
addAll(obstacles);
add(box);
}});
}
public void execude() {
long now = System.currentTimeMillis();
setOldPointsToInf();
work();
Date date = new Date(System.currentTimeMillis() - now);
SimpleDateFormat sdf = new SimpleDateFormat("mm:ss:SSS");
System.out.println(sdf.format(date) + " Minuten:Sekunden:Millisekunden");
}
/*
Berechnet die maximale Dichte des Szenarios anhand von Stichproben
*/
private double calculateMaxDensity(Function<VPoint, Double> density, VRectangle bbox)
{
double maxDensity = 0;
double[][] means = new double[Parameters.SAMPLEDIVISION][Parameters.SAMPLEDIVISION];
Random random = new Random();
for (int i = 0; i < Parameters.SAMPLENUMBER; i++)
{
for (int j = 0; j < Parameters.NPOINTS; j++) {
double x = random.nextInt((int) (bbox.getMaxX() - bbox.getMinX()) + 1);
double y = random.nextInt((int) (bbox.getMaxY() - bbox.getMinY()) + 1);
int xi = (int)Math.floor(x/(bbox.getMaxX()-bbox.getMinX())*(Parameters.SAMPLEDIVISION-1));
int yi = (int)Math.floor(y/(bbox.getMaxY()-bbox.getMinY())*(Parameters.SAMPLEDIVISION-1));
means[yi][xi] = (means[yi][xi] + density.apply(new VPoint(x, y)))/2;
if(maxDensity < means[yi][xi])
maxDensity = means[yi][xi];
}
}
return maxDensity;
}
/*
Stellt den Verlauf der Iterationen dar. Innerhalb der while(true) passiert eine Iteration des Algorithmus
*/
private void work()
{
while(true)
{
//double maxMove = largeMovement();
if(largeMovement())
{
copyPoints();
long now = System.currentTimeMillis();
triangulation = new BowyerWatson3(points);
triangulation.execute();
//System.out.println("different edges:" + triangulation.getTriangles().stream().flatMap(t -> t.getLineStream()).collect(Collectors.toSet()).size());
Set<VLine> setLine = new HashSet<>(triangulation.getTriangles().stream().flatMap(t -> t.getLineStream()).collect(Collectors.toList()));
System.out.println("different edges:" + setLine.size());
//triangulation.setTriangles(triangulation.getTriangles().parallelStream().filter(p ->
// fd.apply(p.midPoint()) < -geps).collect(Collectors.toList()));
int size = triangulation.getTriangles().size();
/*triangulation.removeTriangleIf(triple -> fd.apply(
new VTriangle(triple.getLeft(), triple.getMiddle(), triple.getRight()).midPoint()) >= -geps);*/
triangulation.setTriangles(triangulation.getTriangles().parallelStream().filter(p ->
fd.apply(p.midPoint()) < -geps).collect(Collectors.toList()));
System.out.println("deleted:" + (size - triangulation.getTriangles().size()));
Date date = new Date(System.currentTimeMillis() - now);
System.out.println(new SimpleDateFormat("mm:ss:SSS").format(date) + " Bowyer-Watson-Algo");
}
long now = System.currentTimeMillis();
HashMap<VLine, Integer> reversedLines = createBars();
Date date = new Date(System.currentTimeMillis() - now);
System.out.println(new SimpleDateFormat("mm:ss:SSS").format(date) + " CreateBars");
now = System.currentTimeMillis();
List<VLine> lines = new ArrayList<>(reversedLines.keySet());
date = new Date(System.currentTimeMillis() - now);
System.out.println(new SimpleDateFormat("mm:ss:SSS").format(date) + " CompleteLineList");
now = System.currentTimeMillis();
List<VPoint> FVec = generateFVec(lines);
date = new Date(System.currentTimeMillis() - now);
System.out.println(new SimpleDateFormat("mm:ss:SSS").format(date) + " CalculateFVec");
now = System.currentTimeMillis();
HashMap<String, Double> Ftot = generateFtot(reversedLines, lines, FVec);
date = new Date(System.currentTimeMillis() - now);
System.out.println(new SimpleDateFormat("mm:ss:SSS").format(date) + " CalculateFTot");
now = System.currentTimeMillis();
double[][] array = createFTotArray(Ftot, FVec, points.size());
date = new Date(System.currentTimeMillis() - now);
System.out.println(new SimpleDateFormat("mm:ss:SSS").format(date) + " CreateArrayOfFTot");
now = System.currentTimeMillis();
doEuler(array);
date = new Date(System.currentTimeMillis() - now);
System.out.println(new SimpleDateFormat("mm:ss:SSS").format(date) + " Euler-Method");
now = System.currentTimeMillis();
projectBack();
date = new Date(System.currentTimeMillis() - now);
System.out.println(new SimpleDateFormat("mm:ss:SSS").format(date) + " ProjectPointsBack");
now = System.currentTimeMillis();
double test = qualityCheck();
date = new Date(System.currentTimeMillis() - now);
System.out.println(new SimpleDateFormat("mm:ss:SSS").format(date) + " QualityCheck");
System.out.println(test);
if(test > Parameters.qualityMeasurement)
break;
}
}
/*
Euler-Methode
*/
private void doEuler(double[][] array)