174 lines
5.9 KiB
Java
174 lines
5.9 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.ArrayList;
|
||
|
import java.util.Collection;
|
||
|
import java.util.Collections;
|
||
|
import java.util.HashSet;
|
||
|
import java.util.LinkedList;
|
||
|
import java.util.List;
|
||
|
import java.util.Set;
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|