156 lines
5.5 KiB
Java
156 lines
5.5 KiB
Java
package de.wwwu.awolf.presenter.util;
|
|
|
|
import com.google.common.collect.Lists;
|
|
import de.wwwu.awolf.model.Line;
|
|
import de.wwwu.awolf.model.Point;
|
|
|
|
import java.util.*;
|
|
import java.util.concurrent.ForkJoinPool;
|
|
import java.util.concurrent.ForkJoinTask;
|
|
import java.util.concurrent.RecursiveTask;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.stream.Collectors;
|
|
|
|
/**
|
|
* Implementierung verschiedener Algorithmen zur Berechnung von Ausgleichsgeraden.
|
|
*
|
|
* @Author: Armin Wolf
|
|
* @Email: a_wolf28@uni-muenster.de
|
|
* @Date: 18.09.2017.
|
|
*/
|
|
public class IntersectionComputer {
|
|
|
|
private static IntersectionComputer instance;
|
|
|
|
/**
|
|
* Konstruktor
|
|
*/
|
|
private IntersectionComputer() {
|
|
}
|
|
|
|
public static IntersectionComputer getInstance() {
|
|
if (instance == null) {
|
|
instance = new IntersectionComputer();
|
|
Logging.logInfo("Created instance of IntersectionComputer");
|
|
}
|
|
|
|
return instance;
|
|
}
|
|
|
|
/**
|
|
* Berechnet zu einer gegebenen Menge von dualen Geraden die Schnittpunkte. Dafür wird ein modifizierter Merge-Sort
|
|
* Algorithmus verwendett. Um die Performance zu steigern wird die Berechnung ab einer passenden Größe auf vier
|
|
* Threads ausgelagert.
|
|
*
|
|
* @param lower untere Schranke
|
|
* @param higher obere Schranke
|
|
* @return Liste der Schnittpunkte
|
|
*/
|
|
public Set<Point> compute(final Collection<Line> lines, final double lower, final double higher) {
|
|
|
|
final Set<Line> fullInput = new HashSet<>(lines);
|
|
final Set<Line> copyInput = new HashSet<>(lines);
|
|
|
|
|
|
if (lower == higher) {
|
|
return Collections.emptySet();
|
|
} else {
|
|
Logging.logDebug("Open ForkJoinPool: lines: " + lines.size() + " I(" + lower + ", " + higher + "]");
|
|
ForkJoinPool pool = ForkJoinPool.commonPool();
|
|
RecursiveComputationTask recursiveComputationTask = new RecursiveComputationTask(fullInput, copyInput, lower, higher);
|
|
pool.execute(recursiveComputationTask);
|
|
Set<Point> join = recursiveComputationTask.join();
|
|
return join;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Berechnet die Schnittpunkte zwischen einer gegebenen Gerade und einer Menge an Geraden.
|
|
*
|
|
* @param set Menge an Geraden
|
|
* @param sampledLine eine spezielle Gerade
|
|
* @return Liste mit x Koordinaten der Schnittpunkte
|
|
*/
|
|
public List<Double> calculateIntersectionAbscissas(Collection<Line> set, Line sampledLine, double lower, double upper) {
|
|
List<Line> lines = new LinkedList<>(set);
|
|
Set<Double> intersections = new HashSet<>();
|
|
|
|
for (Line line : lines) {
|
|
if (line != sampledLine) {
|
|
if (sampledLine.doIntersect(line, lower, upper)) {
|
|
intersections.add(sampledLine.intersect(line).getX());
|
|
}
|
|
}
|
|
}
|
|
|
|
return new ArrayList<>(intersections);
|
|
}
|
|
|
|
private class RecursiveComputationTask extends RecursiveTask<Set<Point>> {
|
|
|
|
private static final int THRESHOLD = 200;
|
|
|
|
private final Set<Line> lines;
|
|
private final Set<Line> fullList;
|
|
private final double lower;
|
|
private final double upper;
|
|
|
|
public RecursiveComputationTask(final Set<Line> fullList, final Set<Line> lines, final double lower, final double upper) {
|
|
this.lines = lines;
|
|
this.fullList = fullList;
|
|
this.lower = lower;
|
|
this.upper = upper;
|
|
}
|
|
|
|
@Override
|
|
protected Set<Point> compute() {
|
|
if (this.lines.isEmpty()) {
|
|
return Collections.emptySet();
|
|
} else if (this.lines.size() > THRESHOLD) {
|
|
return ForkJoinTask.invokeAll(createSubTask()).stream().map(ForkJoinTask::join).flatMap(Collection::stream).collect(Collectors.toSet());
|
|
} else {
|
|
return work(this.fullList, this.lines, this.lower, this.upper);
|
|
}
|
|
}
|
|
|
|
private Collection<RecursiveComputationTask> createSubTask() {
|
|
List<RecursiveComputationTask> dividedTasks = new ArrayList<>();
|
|
|
|
long midpoint = Math.round(this.lines.size() * 0.5);
|
|
|
|
Set<Line> firstSubSet = new HashSet<>();
|
|
Set<Line> secondSubSet = new HashSet<>();
|
|
AtomicInteger count = new AtomicInteger();
|
|
this.lines.forEach(next -> {
|
|
int index = count.getAndIncrement();
|
|
if (index < midpoint) {
|
|
firstSubSet.add(next);
|
|
} else {
|
|
secondSubSet.add(next);
|
|
}
|
|
});
|
|
|
|
dividedTasks.add(new RecursiveComputationTask(this.fullList, firstSubSet, this.lower, this.upper));
|
|
dividedTasks.add(new RecursiveComputationTask(this.fullList, secondSubSet, this.lower, this.upper));
|
|
return dividedTasks;
|
|
}
|
|
|
|
private Set<Point> work(Set<Line> fullList, Set<Line> lines, double lower, double higher) {
|
|
Set<Point> points = new HashSet<>();
|
|
List<List<Line>> lists = Lists.cartesianProduct(new ArrayList<>(fullList), new ArrayList<>(lines));
|
|
lists.forEach(entry -> {
|
|
if (entry.get(0).doIntersect(entry.get(1), lower, upper)) {
|
|
Point intersect = entry.get(0).intersect(entry.get(1));
|
|
if (intersect.getX() > lower && intersect.getX() <= higher) {
|
|
points.add(intersect);
|
|
}
|
|
}
|
|
});
|
|
return new HashSet<>(points);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|