Commit b8da33b0 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen
Browse files

implementation of vertex deletion in a Delaunay-triangulation.

parent 17ae5bd4
...@@ -46,6 +46,10 @@ public class GenEar< ...@@ -46,6 +46,10 @@ public class GenEar<
this.edges.set(0, e); this.edges.set(0, e);
} }
public void setMiddle(@NotNull final E e) {
this.edges.set(1, e);
}
private double getPower() { private double getPower() {
return power; return power;
} }
......
...@@ -670,7 +670,16 @@ public class IncrementalTriangulation<P extends IPoint, CE, CF, V extends IVerte ...@@ -670,7 +670,16 @@ public class IncrementalTriangulation<P extends IPoint, CE, CF, V extends IVerte
@Override @Override
public void remove(P point) { public void remove(P point) {
throw new UnsupportedOperationException("not jet implemented."); Optional<F> optFace = locateFace(point);
if(optFace.isPresent()) {
F face = optFace.get();
for(V vertex : getMesh().getVertexIt(face)) {
if(getMesh().getPoint(vertex).equals(point)) {
remove(vertex);
break;
}
}
}
} }
public Collection<VTriangle> getTriangles() { public Collection<VTriangle> getTriangles() {
......
...@@ -146,13 +146,13 @@ public class MeshRenderer<P extends IPoint, CE, CF, V extends IVertex<P>, E exte ...@@ -146,13 +146,13 @@ public class MeshRenderer<P extends IPoint, CE, CF, V extends IVertex<P>, E exte
if(colorFunction != null) { if(colorFunction != null) {
graphics.setColor(colorFunction.apply(face)); graphics.setColor(colorFunction.apply(face));
graphics.fill(polygon); //graphics.fill(polygon);
graphics.setColor(Color.GRAY); graphics.setColor(Color.GRAY);
graphics.draw(polygon); graphics.draw(polygon);
} }
if(alertPred.test(face)) { if(alertPred.test(face)) {
graphics.setColor(new Color(100, 0, 0)); graphics.setColor(new Color(100, 0, 0));
graphics.fill(polygon); //graphics.fill(polygon);
graphics.setColor(Color.GRAY); graphics.setColor(Color.GRAY);
graphics.draw(polygon); graphics.draw(polygon);
......
...@@ -13,6 +13,8 @@ import org.vadere.util.geometry.shapes.IPoint; ...@@ -13,6 +13,8 @@ import org.vadere.util.geometry.shapes.IPoint;
*/ */
public class PFace<P extends IPoint, CE, CF> implements IFace<CF>, Cloneable { public class PFace<P extends IPoint, CE, CF> implements IFace<CF>, Cloneable {
private static int MAX_FACE_PRINT_LEN = 100000;
/** /**
* One of the half-edges bordering this face. * One of the half-edges bordering this face.
*/ */
...@@ -105,13 +107,21 @@ public class PFace<P extends IPoint, CE, CF> implements IFace<CF>, Cloneable { ...@@ -105,13 +107,21 @@ public class PFace<P extends IPoint, CE, CF> implements IFace<CF>, Cloneable {
@Override @Override
public String toString() { public String toString() {
if(destroyed) {
return "destroyed Face";
}
PHalfEdge<P, CE, CF> current = edge; PHalfEdge<P, CE, CF> current = edge;
PHalfEdge<P, CE, CF> next = edge.getNext(); PHalfEdge<P, CE, CF> next = edge.getNext();
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
while (!edge.equals(next)) { int count = 0;
while (count <= MAX_FACE_PRINT_LEN && !edge.equals(next)) {
builder.append(current + " "); builder.append(current + " ");
current = next; current = next;
next = current.getNext(); next = current.getNext();
count++;
}
if(count > MAX_FACE_PRINT_LEN) {
builder.insert(0, "LARGE-FACE:");
} }
builder.append(current); builder.append(current);
return builder.toString(); return builder.toString();
......
...@@ -143,6 +143,9 @@ public class PHalfEdge<P extends IPoint, CE, CF> implements IHalfEdge<CE>, Clone ...@@ -143,6 +143,9 @@ public class PHalfEdge<P extends IPoint, CE, CF> implements IHalfEdge<CE>, Clone
@Override @Override
public String toString() { public String toString() {
if(destroyed) {
return "destroyed half-edge";
}
return getEnd().toString(); return getEnd().toString();
} }
......
...@@ -66,6 +66,9 @@ public class PVertex<P extends IPoint, CE, CF> implements IVertex<P> { ...@@ -66,6 +66,9 @@ public class PVertex<P extends IPoint, CE, CF> implements IVertex<P> {
@Override @Override
public String toString() { public String toString() {
if(destroyed) {
return "destroyed vertex";
}
return point.toString(); return point.toString();
} }
......
...@@ -26,6 +26,7 @@ import org.vadere.util.geometry.shapes.VPoint; ...@@ -26,6 +26,7 @@ import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VPolygon; import org.vadere.util.geometry.shapes.VPolygon;
import org.vadere.util.geometry.shapes.VRectangle; import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VTriangle; import org.vadere.util.geometry.shapes.VTriangle;
import org.vadere.util.logging.Logger;
import java.awt.geom.Path2D; import java.awt.geom.Path2D;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -119,6 +120,8 @@ public interface IMesh< ...@@ -119,6 +120,8 @@ public interface IMesh<
F extends IFace<CF>> F extends IFace<CF>>
extends Iterable<F>, Cloneable { extends Iterable<F>, Cloneable {
Logger logger = Logger.getLogger(IMesh.class);
/** /**
* construct a new empty mesh. * construct a new empty mesh.
* *
...@@ -1003,6 +1006,14 @@ public interface IMesh< ...@@ -1003,6 +1006,14 @@ public interface IMesh<
return streamFaces().filter(face -> !isBoundary(face)).filter(face -> isAlive(face)).collect(Collectors.toList()); return streamFaces().filter(face -> !isBoundary(face)).filter(face -> isAlive(face)).collect(Collectors.toList());
} }
default List<F> getFacesWithBoundary() {
return streamFacesWithBoundary().filter(face -> isAlive(face)).collect(Collectors.toList());
}
default Stream<F> streamFacesWithBoundary() {
return Streams.concat(streamBoundaries(), streamFaces());
}
default Stream<F> streamBoundaries() { default Stream<F> streamBoundaries() {
return Streams.concat(streamHoles(), Stream.of(getBorder())); return Streams.concat(streamHoles(), Stream.of(getBorder()));
} }
...@@ -1958,6 +1969,142 @@ public interface IMesh< ...@@ -1958,6 +1969,142 @@ public interface IMesh<
} }
} }
/**
* Tests if the mesh is a valid mesh, i.e. all relations between edges, faces and vertices are correct,
* e.g. <tt>getFace(getEdge(face)) == face</tt>.
*
* @return true if the mesh is valid, false otherwise
*/
default boolean isValid() {
String message = "invalid mesh: ";
for(F face : getFacesWithBoundary()) {
int count = 0;
for(E edge : getEdgeIt(face)) {
count++;
if(count > getNumberOfEdges()) {
logger.warn(message + "endless loop in face");
return false;
}
F f = getFace(edge);
if(f == null) {
logger.warn(message + "null face of edge " + edge);
return false;
}
if(!f.equals(face)) {
logger.warn(message + "wrong edge face " + face + "!=" + getFace(edge));
return false;
}
}
if(count < 3) {
logger.warn(message + "number of edges smaller 2");
return false;
}
count = 0;
for(V vertex : getVertexIt(face)) {
if(count > getNumberOfVertices()) {
logger.warn(message + "endless loop in face");
return false;
}
}
}
for(V vertex : getVertices()) {
int count = 0;
E edge = getEdge(vertex);
if(edge == null) {
logger.warn(message + "null edge of vertex " + vertex);
return false;
}
if(!vertex.equals(getVertex(edge))) {
logger.warn(message + "wrong edge vertex " + vertex + "!=" + getVertex(edge));
return false;
}
for(E e : getEdgeIt(vertex)) {
if(count > getNumberOfVertices()) {
logger.warn(message + "endless loop around vertex " + vertex);
return false;
}
if(!vertex.equals(getVertex(e))) {
logger.warn(message + "wrong edge vertex " + vertex + "!=" + getVertex(e));
return false;
}
}
}
for(E edge : getEdges()) {
E twin = getTwin(edge);
E next = getNext(edge);
E prev = getPrev(edge);
V v = getVertex(edge);
F face = getFace(edge);
F twinFace = getFace(twin);
if(twin == null) {
logger.warn(message + "twin is null for " + edge);
return false;
}
if(next == null) {
logger.warn(message + "next is null for " + edge);
return false;
}
if(prev == null) {
logger.warn(message + "prev is null for " + edge);
return false;
}
if(v == null) {
logger.warn(message + "vertex is null for " + edge);
return false;
}
E twinTwin = getTwin(twin);
if(twinTwin == null) {
logger.warn(message + "twin of the twin is null for " + edge);
return false;
}
if(!twinTwin.equals(edge)) {
logger.warn(message + "twin of the twin is not equal to the edge " + edge);
return false;
}
V twinVertex = getVertex(twin);
if(twinVertex == null) {
logger.warn(message + "vertex of the twin is null for " + edge);
return false;
}
if(twinVertex.equals(v)) {
logger.warn(message + "edge ends and starts at the same vertex " + v);
return false;
}
if(twinFace.equals(face)) {
logger.warn(message + "the faces of the edge and its twin are equals");
return false;
}
if(isBoundary(edge) && isBoundary(twin)) {
logger.warn(message + "the faces of the edge and its twin are boundaries");
return false;
}
}
return true;
}
/** /**
* Creates a very simple mesh consisting of two triangles ((-100, 0), (100, 0), (0, 1)) and ((0, -1), (-100, 0), (100, 0)) * Creates a very simple mesh consisting of two triangles ((-100, 0), (100, 0), (0, 1)) and ((0, -1), (-100, 0), (100, 0))
* *
......
...@@ -901,6 +901,8 @@ public interface ITriConnectivity<P extends IPoint, CE, CF, V extends IVertex<P> ...@@ -901,6 +901,8 @@ public interface ITriConnectivity<P extends IPoint, CE, CF, V extends IVertex<P>
assert !getMesh().isAtBoundary(vertex); assert !getMesh().isAtBoundary(vertex);
// (1) remove the vertex // (1) remove the vertex
// get ringEdges in ccw order!
List<E> ringEdges = getMesh().streamEdges(vertex).map(edge -> getMesh().getPrev(edge)).collect(Collectors.toList()); List<E> ringEdges = getMesh().streamEdges(vertex).map(edge -> getMesh().getPrev(edge)).collect(Collectors.toList());
F face = getMesh().getFace(ringEdges.get(ringEdges.size()-1)); F face = getMesh().getFace(ringEdges.get(ringEdges.size()-1));
...@@ -928,35 +930,27 @@ public interface ITriConnectivity<P extends IPoint, CE, CF, V extends IVertex<P> ...@@ -928,35 +930,27 @@ public interface ITriConnectivity<P extends IPoint, CE, CF, V extends IVertex<P>
} }
getMesh().setEdge(face, ringEdges.get(ringEdges.size()-1)); getMesh().setEdge(face, ringEdges.get(ringEdges.size()-1));
getMesh().setFace(ringEdges.get(ringEdges.size()-1), face);
getMesh().destroyVertex(vertex); getMesh().destroyVertex(vertex);
NodeLinkedList<GenEar<P, CE, CF, V, E, F>> list = new NodeLinkedList<>(); NodeLinkedList<GenEar<P, CE, CF, V, E, F>> list = new NodeLinkedList<>();
GenEar.EarNodeComparator<P, CE, CF, V, E, F> comparator = new GenEar.EarNodeComparator<>(); GenEar.EarNodeComparator<P, CE, CF, V, E, F> comparator = new GenEar.EarNodeComparator<>();
PriorityQueue<Node<GenEar<P, CE, CF, V, E, F>>> heap = new PriorityQueue<>(comparator); PriorityQueue<Node<GenEar<P, CE, CF, V, E, F>>> heap = new PriorityQueue<>(comparator);
assert getMesh().isValid();
// (2) re-triangulate // (2) re-triangulate
for(int i = 0; i < ringEdges.size(); i++) { for(int i = 0; i < ringEdges.size(); i++) {
E e1 = ringEdges.get(i % ringEdges.size()); E e1 = ringEdges.get(i % ringEdges.size());
E e2 = ringEdges.get((i+1) % ringEdges.size()); E e2 = ringEdges.get((i+1) % ringEdges.size());
E e3 = ringEdges.get((i+2) % ringEdges.size()); E e3 = ringEdges.get((i+2) % ringEdges.size());
P p = getMesh().getPoint(e1); GenEar<P, CE, CF, V, E, F> ear = new GenEar<>(e1, e2, e3, power(e1, e2, e3, vertex));
double power;
// clockwise
if(isRightOf(p.getX(), p.getY(), e3)) {
power = Double.MAX_VALUE;
}
else {
power = -power(e1, e2, e3, vertex);
}
GenEar<P, CE, CF, V, E, F> ear = new GenEar<>(e1, e2, e3, power);
Node<GenEar<P, CE, CF, V, E, F>> earNode = list.add(ear); Node<GenEar<P, CE, CF, V, E, F>> earNode = list.add(ear);
heap.add(earNode); heap.add(earNode);
} }
while (heap.size() > 2) { while (heap.size() > 3) {
Node<GenEar<P, CE, CF, V, E, F>> earNode = heap.poll(); Node<GenEar<P, CE, CF, V, E, F>> earNode = heap.poll();
GenEar<P, CE, CF, V, E, F> ear = earNode.getElement(); GenEar<P, CE, CF, V, E, F> ear = earNode.getElement();
...@@ -976,17 +970,17 @@ public interface ITriConnectivity<P extends IPoint, CE, CF, V extends IVertex<P> ...@@ -976,17 +970,17 @@ public interface ITriConnectivity<P extends IPoint, CE, CF, V extends IVertex<P>
getMesh().setNext(e, e2); getMesh().setNext(e, e2);
getMesh().setNext(e3, e); getMesh().setNext(e3, e);
getMesh().setFace(e1, f);
getMesh().setFace(e2, f);
getMesh().setFace(e, f); getMesh().setFace(e, f);
getMesh().setFace(e2, f);
getMesh().setFace(e3, f);
getMesh().setPrev(next, t); getMesh().setNext(t, next);
getMesh().setNext(e1, t); getMesh().setNext(e1, t);
getMesh().setFace(t, tf); getMesh().setFace(t, tf);
getMesh().setEdge(tf, t); getMesh().setEdge(tf, t);
// end // end
if(heap.size() > 2) { if(heap.size() > 3) {
Node<GenEar<P, CE, CF, V, E, F>> prevEarNode = earNode.getPrev(); Node<GenEar<P, CE, CF, V, E, F>> prevEarNode = earNode.getPrev();
Node<GenEar<P, CE, CF, V, E, F>> nextEarNode = earNode.getNext(); Node<GenEar<P, CE, CF, V, E, F>> nextEarNode = earNode.getNext();
...@@ -998,33 +992,52 @@ public interface ITriConnectivity<P extends IPoint, CE, CF, V extends IVertex<P> ...@@ -998,33 +992,52 @@ public interface ITriConnectivity<P extends IPoint, CE, CF, V extends IVertex<P>
nextEarNode = list.getHead(); nextEarNode = list.getHead();
} }
Node<GenEar<P, CE, CF, V, E, F>> nnextEarNode = nextEarNode.getNext();
if(nnextEarNode == null) {
nnextEarNode = list.getHead();
}
heap.remove(earNode); heap.remove(earNode);
heap.remove(prevEarNode); heap.remove(prevEarNode);
heap.remove(nextEarNode); heap.remove(nextEarNode);
heap.remove(nnextEarNode);
prevEarNode.getElement().setLast(earNode.getElement().getLast()); prevEarNode.getElement().setLast(t);
nextEarNode.getElement().setFirst(earNode.getElement().getFirst());
nextEarNode.getElement().setFirst(e1);
nextEarNode.getElement().setMiddle(t);
nnextEarNode.getElement().setFirst(t);
earNode.remove(); earNode.remove();
GenEar<P, CE, CF, V, E, F> prevEar = prevEarNode.getElement(); GenEar<P, CE, CF, V, E, F> prevEar = prevEarNode.getElement();
GenEar<P, CE, CF, V, E, F> nextEar = nextEarNode.getElement(); GenEar<P, CE, CF, V, E, F> nextEar = nextEarNode.getElement();
GenEar<P, CE, CF, V, E, F> nnextEar = nnextEarNode.getElement();
prevEar.setPower(power(prevEar.getEdges().get(0), prevEar.getEdges().get(1), prevEar.getEdges().get(2), vertex)); prevEar.setPower(power(prevEar.getEdges().get(0), prevEar.getEdges().get(1), prevEar.getEdges().get(2), vertex));
nextEar.setPower(power(nextEar.getEdges().get(0), nextEar.getEdges().get(1), nextEar.getEdges().get(2), vertex)); nextEar.setPower(power(nextEar.getEdges().get(0), nextEar.getEdges().get(1), nextEar.getEdges().get(2), vertex));
nnextEar.setPower(power(nnextEar.getEdges().get(0), nnextEar.getEdges().get(1), nnextEar.getEdges().get(2), vertex));
heap.add(prevEarNode); heap.add(prevEarNode);
heap.add(nextEarNode); heap.add(nextEarNode);
heap.add(nnextEarNode);
} }
} }
} }
default double power(@NotNull final E e1, @NotNull final E e2, @NotNull final E e3, @NotNull final IPoint p) { private double power(@NotNull final E e1, @NotNull final E e2, @NotNull final E e3, @NotNull final IPoint p) {
P point = getMesh().getPoint(e1);
if(!isLeftOf(point.getX(), point.getY(), e3)) {
return Double.MAX_VALUE;
}
VPoint p1 = getMesh().toPoint(getMesh().getPoint(e1)); VPoint p1 = getMesh().toPoint(getMesh().getPoint(e1));
VPoint p2 = getMesh().toPoint(getMesh().getPoint(e2)); VPoint p2 = getMesh().toPoint(getMesh().getPoint(e2));
VPoint p3 = getMesh().toPoint(getMesh().getPoint(e3)); VPoint p3 = getMesh().toPoint(getMesh().getPoint(e3));
VTriangle triangle = new VTriangle(p1, p2, p3); VTriangle triangle = new VTriangle(p1, p2, p3);
VPoint x = triangle.getCircumcenter(); VPoint x = triangle.getCircumcenter();
double r = triangle.getCircumscribedRadius(); double r = triangle.getCircumscribedRadius();
double xp = new VLine(x, new VPoint(p)).length(); double xpSq = x.distanceSq(p);
return xp * xp - r*r; double power = (xpSq - r*r);
return -power;
} }
/** /**
......
...@@ -39,19 +39,19 @@ public class IncidentEdgeIterator<P extends IPoint, CE, CF, V extends IVertex<P> ...@@ -39,19 +39,19 @@ public class IncidentEdgeIterator<P extends IPoint, CE, CF, V extends IVertex<P>
public IncidentEdgeIterator(final IMesh<P, CE, CF, V, E, F> mesh, final E edge) { public IncidentEdgeIterator(final IMesh<P, CE, CF, V, E, F> mesh, final E edge) {
this.mesh = mesh; this.mesh = mesh;
this.edge = edge; this.edge = edge;
this.current = mesh.getNext(edge); this.current = mesh.getTwin(edge);
this.first = true; this.first = true;
} }
@Override @Override
public boolean hasNext() { public boolean hasNext() {
return (first || current != mesh.getNext(edge)); return (first || current != mesh.getTwin(edge));
} }
@Override @Override
public E next() { public E next() {
E result = current; E result = current;
current = mesh.getNext(mesh.getTwin(result)); current = mesh.getTwin(mesh.getPrev(result));
first = false; first = false;
count++; count++;
//log.info(count); //log.info(count);
......
package org.vadere.geometry.mesh; package org.vadere.geometry.mesh;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.vadere.meshing.mesh.gen.PFace; import org.vadere.meshing.mesh.gen.PFace;
import org.vadere.meshing.mesh.gen.PHalfEdge;