Commit 38622449 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen

software architecture improvement for the triangulation

parent 8f2bf247
......@@ -534,6 +534,22 @@ public class GeometryUtils {
return new VRectangle(pMin.getX(), pMin.getY(), pMax.getX() - pMin.getX(), pMax.getY() - pMin.getY());
}
public static double sign(final double x1, final double y1, final double x2, final double y2, final double x3, final double y3) {
return (x1 - x3) * (y2 - y3) - (x2 -x3) * (y1 - y3);
}
public static <P extends IPoint> VRectangle bound(final Collection<P> points) {
if(points.isEmpty()) {
throw new IllegalArgumentException("the point collection is empty.");
}
VPoint pMax = points.stream().map(p -> new VPoint(p.getX(), p.getY())).reduce((p1, p2) -> new VPoint(Math.max(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()))).get();
VPoint pMin = points.stream().map(p -> new VPoint(p.getX(), p.getY())).reduce((p1, p2) -> new VPoint(Math.min(p1.getX(), p2.getX()), Math.min(p1.getY(), p2.getY()))).get();
return new VRectangle(pMin.getX(), pMin.getY(), pMax.getX() - pMin.getX(), pMax.getY() - pMin.getY());
}
/**
* This method follows the construction from
* https://proofwiki.org/wiki/Obtuse_Triangle_Divided_into_Acute_Triangles
......
......@@ -82,16 +82,6 @@ public class PMesh<P extends IPoint> implements IMesh<P, PHalfEdge<P>, PFace<P>>
return halfEdge.isBoundary();
}
@Override
public boolean isHole(@NotNull PFace<P> face) {
return false;
}
@Override
public boolean isHole(@NotNull PHalfEdge<P> halfEdge) {
return false;
}
@Override
public boolean isDestroyed(@NotNull PFace<P> face) {
return face.isDestroyed();
......@@ -152,6 +142,11 @@ public class PMesh<P extends IPoint> implements IMesh<P, PHalfEdge<P>, PFace<P>>
return vertices.size();
}
@Override
public int getNumberOfFaces() {
return faces.size();
}
@Override
public PHalfEdge<P> createEdge(@NotNull P vertex) {
PHalfEdge<P> edge = new PHalfEdge<>(vertex);
......@@ -190,6 +185,11 @@ public class PMesh<P extends IPoint> implements IMesh<P, PHalfEdge<P>, PFace<P>>
return vertex;
}
@Override
public PFace<P> getBoundary() {
return boundary;
}
@Override
public void insert(P vertex) {
vertices.add(vertex);
......
......@@ -33,7 +33,7 @@ public interface IPolyConnectivity<P extends IPoint, E extends IHalfEdge<P>, F e
return Optional.empty();
}
default Optional<F> locate(final IPoint point) {
default Optional<F> locate(final P point) {
return locate(point.getX(), point.getY());
}
......
......@@ -26,7 +26,7 @@ public interface ITriConnectivity<P extends IPoint, E extends IHalfEdge<P>, F ex
default void flipEdgeEvent(F f1, F f2) {}
default void insertEvent(P vertex) {};
default void insertEvent(E vertex) {};
boolean isIllegal(E edge);
......@@ -381,13 +381,18 @@ public interface ITriConnectivity<P extends IPoint, E extends IHalfEdge<P>, F ex
}
@Override
default Optional<F> locate(final IPoint point) {
default Optional<F> locate(final P point) {
return this.locate(point.getX(), point.getY());
}
@Override
default Optional<F> locate(final double x, final double y) {
return locate(x, y, getMesh().getFace());
if(getMesh().getNumberOfFaces() > 0) {
return locate(x, y, getMesh().getFace());
}
else {
return Optional.empty();
}
}
/*default Optional<P> locateVertex(double x, double y, F startFace) {
......
......@@ -26,7 +26,7 @@ import java.util.Optional;
*/
public class BasePointLocator<P extends IPoint, E extends IHalfEdge<P>, F extends IFace<P>> implements IPointLocator<P, E, F> {
private final ITriConnectivity<P, E, F> triConnectivity;
private ITriConnectivity<P, E, F> triConnectivity;
public BasePointLocator(final ITriConnectivity<P, E, F> triConnectivity) {
this.triConnectivity = triConnectivity;
......@@ -39,7 +39,7 @@ public class BasePointLocator<P extends IPoint, E extends IHalfEdge<P>, F extend
public void flipEdgeEvent(final F f1, final F f2) {}
@Override
public void insertEvent(final P vertex) {}
public void insertEvent(E vertex) {}
@Override
public void deleteBoundaryFace(final F face) {}
......
......@@ -2,24 +2,50 @@ package org.vadere.util.geometry.mesh.triangulations;
import org.vadere.util.geometry.mesh.inter.IFace;
import org.vadere.util.geometry.mesh.inter.IHalfEdge;
import org.vadere.util.geometry.mesh.inter.IMesh;
import org.vadere.util.geometry.mesh.inter.IPointLocator;
import org.vadere.util.geometry.mesh.inter.ITriConnectivity;
import org.vadere.util.geometry.mesh.inter.ITriangulation;
import org.vadere.util.geometry.shapes.IPoint;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.function.Supplier;
/**
* Created by bzoennchen on 21.04.17.
* @author Benedikt Zoennchen
*
* @param <P>
* @param <E>
* @param <F>
*/
public class DelaunayHierarchy<P extends IPoint, E extends IHalfEdge<P>, F extends IFace<P>> implements IPointLocator<P, E, F> {
//private List<ITriangulation<P, E, F>>
private List<ITriangulation<P, E, F>> hierarchySets;
private ITriConnectivity<P, E, F> triConnectivity;
private List<Map<E, E>> hierarchyConnector;
public DelaunayHierarchy(final ITriConnectivity<P, E, F> triConnectivity) {
this.triConnectivity = triConnectivity;
private ITriConnectivity<P, E, F> base;
private Supplier<ITriangulation<P, E, F>> triangulationSupplier;
private double alpha;
private Random random;
public DelaunayHierarchy(final ITriangulation<P, E, F> base, final Supplier<ITriangulation<P, E, F>> triangulationSupplier) {
this.hierarchySets = new ArrayList<>();
this.hierarchyConnector = new ArrayList<>();
this.random = new Random();
this.triangulationSupplier = triangulationSupplier;
hierarchySets.add(base);
hierarchyConnector.add(new HashMap<>());
}
@Override
......@@ -29,8 +55,31 @@ public class DelaunayHierarchy<P extends IPoint, E extends IHalfEdge<P>, F exten
public void flipEdgeEvent(F f1, F f2) {}
@Override
public void insertEvent(P vertex) {
public void insertEvent(final E halfEdge) {
P vertex = base.getMesh().getVertex(halfEdge);
E lastEdge = halfEdge;
for(int i = 1; i < hierarchySets.size(); ++i) {
if(random.nextDouble() < alpha) {
if(hierarchySets.size() <= i) {
hierarchySets.add(triangulationSupplier.get());
}
E edge = hierarchySets.get(i).insert(vertex);
if(hierarchyConnector.size() < i) {
hierarchyConnector.add(new HashMap<>());
}
hierarchyConnector.get(i-1).put(lastEdge, edge);
lastEdge = edge;
}
else {
break;
}
}
}
@Override
......@@ -39,12 +88,33 @@ public class DelaunayHierarchy<P extends IPoint, E extends IHalfEdge<P>, F exten
}
@Override
public Collection<F> locatePoint(IPoint point, boolean insertion) {
public Collection<F> locatePoint(P point, boolean insertion) {
return null;
}
@Override
public Optional<F> locate(IPoint point) {
return null;
public Optional<F> locate(final P point) {
Optional<F> optStartFace = Optional.empty();
for(int i = hierarchySets.size()-1; i >= 0; --i) {
if(!optStartFace.isPresent()) {
optStartFace = hierarchySets.get(i).locate(point.getX(), point.getY());
}
else {
E edge = getNearestPoint(hierarchySets.get(i-1), optStartFace.get(), point);
E newEdge = hierarchyConnector.get(i-1).get(edge);
optStartFace = hierarchySets.get(i).locate(point.getX(), point.getY(), hierarchySets.get(i).getMesh().getFace(newEdge));
if(!optStartFace.isPresent()) {
return Optional.empty();
}
}
}
return optStartFace;
}
public E getNearestPoint(final ITriangulation<P, E, F> triangulation, final F face, final P point) {
IMesh<P, E, F> mesh = triangulation.getMesh();
return triangulation.getMesh().streamEdges(face).reduce((p1, p2) -> mesh.getVertex(p1).distance(point) > mesh.getVertex(p2).distance(point) ? p2 : p1).get();
}
}
......@@ -24,16 +24,27 @@ public class DelaunayTree<P extends IPoint, E extends IHalfEdge<P>, F extends IF
private final IMesh<P, E, F> mesh;
private double eps = 0.0000001;
public DelaunayTree(final ITriangulation<P, E, F> triangulation, final F superTriangle) {
public DelaunayTree(final ITriangulation<P, E, F> triangulation) {
this.mesh = triangulation.getMesh();
this.map = new HashMap<>();
}
private void checkRoot() {
if(dag == null) {
F face = mesh.getFace();
this.dag = new DAG<>(new DAGElement<>(superTriangle, mesh.toTriple(superTriangle)));
this.map.put(superTriangle, dag);
if(mesh.isBoundary(face)) {
face = mesh.getTwinFace(mesh.getEdge(face));
}
this.dag = new DAG<>(new DAGElement<>(face, mesh.toTriple(face)));
this.map.put(face, dag);
}
}
@Override
public Collection<F> locatePoint(final IPoint point, final boolean insertion) {
public Collection<F> locatePoint(final P point, final boolean insertion) {
checkRoot();
Set<DAG<DAGElement<P, F>>> leafs = new HashSet<>();
LinkedList<DAG<DAGElement<P, F>>> nodesToVisit = new LinkedList<>();
......@@ -60,7 +71,8 @@ public class DelaunayTree<P extends IPoint, E extends IHalfEdge<P>, F extends IF
}
@Override
public Optional<F> locate(final IPoint point) {
public Optional<F> locate(final P point) {
checkRoot();
Optional<F> optFace = locatePoint(point, false).stream().findAny();
if(optFace.isPresent()) {
return Optional.of(optFace.get());
......@@ -72,6 +84,7 @@ public class DelaunayTree<P extends IPoint, E extends IHalfEdge<P>, F extends IF
@Override
public void splitFaceEvent(F original, F[] faces) {
checkRoot();
DAG<DAGElement<P, F>> faceDag = map.remove(original);
for(F face : faces) {
List<P> points = mesh.getVertices(face);
......@@ -83,6 +96,7 @@ public class DelaunayTree<P extends IPoint, E extends IHalfEdge<P>, F extends IF
@Override
public void flipEdgeEvent(final F f1, final F f2) {
checkRoot();
DAG<DAGElement<P, F>> f1Dag = map.remove(f1);
DAG<DAGElement<P, F>> f2Dag = map.remove(f2);
List<P> points1 = mesh.getVertices(f1);
......@@ -102,10 +116,11 @@ public class DelaunayTree<P extends IPoint, E extends IHalfEdge<P>, F extends IF
}
@Override
public void insertEvent(P vertex) {}
public void insertEvent(E vertex) {}
@Override
public void deleteBoundaryFace(F face) {
checkRoot();
assert mesh.isBoundary(face);
map.remove(face);
}
......
......@@ -3,7 +3,7 @@ package org.vadere.util.geometry.mesh.triangulations;
import org.apache.commons.collections.IteratorUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.vadere.util.geometry.GeometryUtils;
import org.vadere.util.geometry.mesh.impl.PFace;
import org.vadere.util.geometry.mesh.iterators.FaceIterator;
import org.vadere.util.geometry.mesh.inter.IFace;
......@@ -51,11 +51,13 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
private E he0;
private E he1;
private E he2;
private P p_max;
private P p_min;
private boolean finalized;
private final VRectangle bound;
private boolean finalized = false;
private IMesh<P, E, F> mesh;
private IPointLocator<P, E, F> pointLocator;
private boolean initialized = false;
// TODO this epsilon it hard coded!!! => replace it with a user choice
private double epsilon = 0.0001;
protected F superTriangle;
......@@ -64,48 +66,43 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
private static Logger log = LogManager.getLogger(IncrementalTriangulation.class);
public IncrementalTriangulation(
final IMesh<P, E, F> mesh,
final Set<P> points,
final Predicate<E> illegalPredicate) {
this.mesh = mesh;
this.points = points;
this.illegalPredicate = illegalPredicate;
P p_max = points.parallelStream().reduce(mesh.createVertex(Double.MIN_VALUE, Double.MIN_VALUE), (a, b) -> mesh.createVertex(Math.max(a.getX(), b.getX()), Math.max(a.getY(), b.getY())));
P p_min = points.parallelStream().reduce(mesh.createVertex(Double.MIN_VALUE, Double.MIN_VALUE), (a, b) -> mesh.createVertex(Math.min(a.getX(), b.getX()), Math.min(a.getY(), b.getY())));
init(p_max, p_min);
this.bound = GeometryUtils.bound(points);
}
public IncrementalTriangulation(final IMesh<P, E, F> mesh, final Set<P> points) {
this(mesh, points, halfEdge -> true);
public IncrementalTriangulation(final Set<P> points) {
this(points, halfEdge -> true);
}
public IncrementalTriangulation(
final IMesh<P, E, F> mesh,
final double minX,
final double minY,
final double width,
final double height,
final VRectangle bound,
final Predicate<E> illegalPredicate) {
this.mesh = mesh;
this.points = new HashSet<>();
this.illegalPredicate = illegalPredicate;
this.p_max = mesh.createVertex(minX + width, minY + height);
this.p_min = mesh.createVertex(minX, minY);
init(p_max, p_min);
this.bound = bound;
}
public IncrementalTriangulation(
final IMesh<P, E, F> mesh,
final double minX,
final double minY,
final double width,
final double height) {
this(mesh, minX, minY, width, height, halfEdge -> true);
public IncrementalTriangulation(final VRectangle bound) {
this(bound, halfEdge -> true);
}
public void setPointLocator(final IPointLocator<P, E, F> pointLocator) {
this.pointLocator = pointLocator;
}
public void setMesh(final IMesh<P, E, F> mesh) {
this.mesh = mesh;
}
private void init(final P p_max, final P p_min) {
VRectangle bound = new VRectangle(p_min.getX(), p_min.getY(), p_max.getX()-p_min.getX(), p_max.getY()- p_min.getY());
public F getSuperTriangle() {
return superTriangle;
}
@Override
public void init() {
double gap = 1.0;
double max = Math.max(bound.getWidth(), bound.getHeight())*2;
p0 = mesh.insertVertex(bound.getX() - max - gap, bound.getY() - gap);
......@@ -120,14 +117,15 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
he1 = borderEdges.get(1);
he2 = borderEdges.get(2);
// this.pointLocator = IPointLocator.createDelaunayTree(this, superTriangle);
this.pointLocator = IPointLocator.createBaseLocator(this);
this.finalized = false;
this.initialized = true;
}
@Override
public void compute() {
init();
// 1. insert points
for(P p : points) {
insert(p);
......@@ -155,14 +153,16 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
F face = faces.iterator().next();
splitTriangle(face, point, true);
insertedEdge = mesh.getEdge(point);
insertEvent(insertedEdge);
} // point lies on an edge of 2 triangles
else if(faces.size() == 2) {
Iterator<F> it = faces.iterator();
log.info("splitEdge:" + point);
E halfEdge = findTwins(it.next(), it.next()).get();
splitEdge(point, halfEdge, true);
insertedEdge = mesh.getEdge(point);
List<E> newEdges = splitEdge(point, halfEdge, true);
insertedEdge = newEdges.stream().filter(he -> getMesh().getVertex(he).equals(point)).findAny().get();
insertEvent(insertedEdge);
}
else if(faces.size() == 0) {
log.warn("ignore insertion point, since the point " + point + " already exists or it is too close to another point!");
......@@ -174,6 +174,18 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
return insertedEdge;
}
@Override
public void insert(final Set<P> points) {
if(!initialized) {
init();
}
// 1. insert points
for(P p : points) {
insert(p);
}
}
/**
* Removes the super triangle from the mesh data structure.
*/
......@@ -282,7 +294,7 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
}
@Override
public Optional<F> locate(final IPoint point) {
public Optional<F> locate(final P point) {
return pointLocator.locate(point);
}
......@@ -296,6 +308,11 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
return stream();
}
@Override
public Stream<VTriangle> streamTriangles() {
return stream().map(f -> getMesh().toTriangle(f));
}
@Override
public void remove(P point) {
throw new UnsupportedOperationException("not jet implemented.");
......@@ -341,9 +358,7 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
P x = mesh.getVertex(t0);
P y = mesh.getVertex(t1);
P z = mesh.getVertex(t2);
if(x.equals(y)) {
System.out.print("error");
}
VTriangle triangle = new VTriangle(new VPoint(x), new VPoint(y), new VPoint(z));
return triangle.isInCircumscribedCycle(p);
}
......@@ -374,8 +389,8 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
}
@Override
public void insertEvent(final P vertex) {
pointLocator.insertEvent(vertex);
public void insertEvent(final E halfEdge) {
pointLocator.insertEvent(halfEdge);
}
@Override
......@@ -408,8 +423,13 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
IPointConstructor<VPoint> pointConstructor = (x, y) -> new VPoint(x, y);
long ms = System.currentTimeMillis();
IncrementalTriangulation<VPoint, PHalfEdge<VPoint>, PFace<VPoint>> bw = new IncrementalTriangulation<>(new PMesh<>(pointConstructor), points);
PMesh<VPoint> mesh = new PMesh<>(pointConstructor);
IncrementalTriangulation<VPoint, PHalfEdge<VPoint>, PFace<VPoint>> bw = new IncrementalTriangulation<>(points);
bw.setMesh(mesh);
bw.setPointLocator(new DelaunayTree<>(bw));
bw.compute();
Set<VLine> edges = bw.getEdges();
edges.addAll(bw.getTriangles().stream().map(triangle -> new VLine(triangle.getIncenter(), triangle.p1)).collect(Collectors.toList()));
System.out.println(System.currentTimeMillis() - ms);
......@@ -433,9 +453,13 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
window2.getContentPane().add(new Lines(edges2, points, max));
window2.setVisible(true);
UniformTriangulation<VPoint, PHalfEdge<VPoint>, PFace<VPoint>> uniformTriangulation = new UniformTriangulation<>(new PMesh<>(pointConstructor),0, 0, width, height, 10.0);
UniformTriangulation<VPoint, PHalfEdge<VPoint>, PFace<VPoint>> uniformTriangulation = ITriangulation.createUnifirmTriangulation(
IPointLocator.Type.DELAUNAY_TREE,
new VRectangle(0, 0, width, height),
10.0,
(x, y) -> new VPoint(x, y));
uniformTriangulation.compute();
uniformTriangulation.finalize();
Set<VLine> edges3 = uniformTriangulation.getEdges();
JFrame window3 = new JFrame();
......@@ -444,7 +468,12 @@ public class IncrementalTriangulation<P extends IPoint, E extends IHalfEdge<P>,
window3.getContentPane().add(new Lines(edges3, edges3.stream().flatMap(edge -> edge.streamPoints()).collect(Collectors.toSet()), max));
window3.setVisible(true);
UniformRefinementTriangulation<VPoint, PHalfEdge<VPoint>, PFace<VPoint>> uniformRefinement = new UniformRefinementTriangulation<>(new PMesh<>(pointConstructor), 0, 0, width, height, Arrays.asList(new VRectangle(200, 200, 100, 200)), p -> 10.0);
UniformRefinementTriangulation<VPoint> uniformRefinement = new UniformRefinementTriangulation<>(
new VRectangle(0, 0, width, height),
Arrays.asList(new VRectangle(200, 200, 100, 200)),
p -> 10.0,
(x, y) -> new VPoint(x, y));
uniformRefinement.compute();
Set<VLine> edges4 = uniformRefinement.getEdges();
......
......@@ -2,9 +2,11 @@ package org.vadere.util.geometry.mesh.triangulations;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.vadere.util.geometry.mesh.inter.IFace;
import org.vadere.util.geometry.mesh.inter.IHalfEdge;
import org.vadere.util.geometry.mesh.impl.PFace;
import org.vadere.util.geometry.mesh.impl.PHalfEdge;
import org.vadere.util.geometry.mesh.inter.IMesh;
import org.vadere.util.geometry.mesh.inter.IPointLocator;
import org.vadere.util.geometry.mesh.inter.ITriangulation;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VLine;
import org.vadere.util.geometry.shapes.VPoint;
......@@ -18,7 +20,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
......@@ -27,33 +28,30 @@ import java.util.Set;
*
* @param <P>
*/
public class UniformRefinementTriangulation<P extends IPoint, E extends IHalfEdge<P>, F extends IFace<P>> {
public class UniformRefinementTriangulation<P extends IPoint> {
private final Collection<VShape> boundary;
private final VRectangle bbox;
private final IEdgeLengthFunction lenFunc;
private IncrementalTriangulation<P, E, F> triangulation;
private ITriangulation<P, PHalfEdge<P>, PFace<P>> pTriangulation;
private Set<P> points;
private IMesh<P, E, F> mesh;
private IMesh<P, PHalfEdge<P>, PFace<P>> mesh;