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<
this.edges.set(0, e);
}
public void setMiddle(@NotNull final E e) {
this.edges.set(1, e);
}
private double getPower() {
return power;
}
......
......@@ -670,7 +670,16 @@ public class IncrementalTriangulation<P extends IPoint, CE, CF, V extends IVerte
@Override
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() {
......
......@@ -146,13 +146,13 @@ public class MeshRenderer<P extends IPoint, CE, CF, V extends IVertex<P>, E exte
if(colorFunction != null) {
graphics.setColor(colorFunction.apply(face));
graphics.fill(polygon);
//graphics.fill(polygon);
graphics.setColor(Color.GRAY);
graphics.draw(polygon);
}
if(alertPred.test(face)) {
graphics.setColor(new Color(100, 0, 0));
graphics.fill(polygon);
//graphics.fill(polygon);
graphics.setColor(Color.GRAY);
graphics.draw(polygon);
......
......@@ -13,6 +13,8 @@ import org.vadere.util.geometry.shapes.IPoint;
*/
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.
*/
......@@ -105,13 +107,21 @@ public class PFace<P extends IPoint, CE, CF> implements IFace<CF>, Cloneable {
@Override
public String toString() {
if(destroyed) {
return "destroyed Face";
}
PHalfEdge<P, CE, CF> current = edge;
PHalfEdge<P, CE, CF> next = edge.getNext();
StringBuilder builder = new StringBuilder();
while (!edge.equals(next)) {
int count = 0;
while (count <= MAX_FACE_PRINT_LEN && !edge.equals(next)) {
builder.append(current + " ");
current = next;
next = current.getNext();
count++;
}
if(count > MAX_FACE_PRINT_LEN) {
builder.insert(0, "LARGE-FACE:");
}
builder.append(current);
return builder.toString();
......
......@@ -143,6 +143,9 @@ public class PHalfEdge<P extends IPoint, CE, CF> implements IHalfEdge<CE>, Clone
@Override
public String toString() {
if(destroyed) {
return "destroyed half-edge";
}
return getEnd().toString();
}
......
......@@ -66,6 +66,9 @@ public class PVertex<P extends IPoint, CE, CF> implements IVertex<P> {
@Override
public String toString() {
if(destroyed) {
return "destroyed vertex";
}
return point.toString();
}
......
......@@ -26,6 +26,7 @@ import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VPolygon;
import org.vadere.util.geometry.shapes.VRectangle;
import org.vadere.util.geometry.shapes.VTriangle;
import org.vadere.util.logging.Logger;
import java.awt.geom.Path2D;
import java.util.ArrayList;
......@@ -119,6 +120,8 @@ public interface IMesh<
F extends IFace<CF>>
extends Iterable<F>, Cloneable {
Logger logger = Logger.getLogger(IMesh.class);
/**
* construct a new empty mesh.
*
......@@ -1003,6 +1006,14 @@ public interface IMesh<
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() {
return Streams.concat(streamHoles(), Stream.of(getBorder()));
}
......@@ -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))
*
......
......@@ -901,6 +901,8 @@ public interface ITriConnectivity<P extends IPoint, CE, CF, V extends IVertex<P>
assert !getMesh().isAtBoundary(vertex);
// (1) remove the vertex
// get ringEdges in ccw order!
List<E> ringEdges = getMesh().streamEdges(vertex).map(edge -> getMesh().getPrev(edge)).collect(Collectors.toList());
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>
}
getMesh().setEdge(face, ringEdges.get(ringEdges.size()-1));
getMesh().setFace(ringEdges.get(ringEdges.size()-1), face);
getMesh().destroyVertex(vertex);
NodeLinkedList<GenEar<P, CE, CF, V, E, F>> list = new NodeLinkedList<>();
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);
assert getMesh().isValid();
// (2) re-triangulate
for(int i = 0; i < ringEdges.size(); i++) {
E e1 = ringEdges.get(i % ringEdges.size());
E e2 = ringEdges.get((i+1) % ringEdges.size());
E e3 = ringEdges.get((i+2) % ringEdges.size());
P p = getMesh().getPoint(e1);
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);
GenEar<P, CE, CF, V, E, F> ear = new GenEar<>(e1, e2, e3, power(e1, e2, e3, vertex));
Node<GenEar<P, CE, CF, V, E, F>> earNode = list.add(ear);
heap.add(earNode);
}
while (heap.size() > 2) {
while (heap.size() > 3) {
Node<GenEar<P, CE, CF, V, E, F>> earNode = heap.poll();
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>
getMesh().setNext(e, e2);
getMesh().setNext(e3, e);
getMesh().setFace(e1, f);
getMesh().setFace(e2, 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().setFace(t, tf);
getMesh().setEdge(tf, t);
// 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>> nextEarNode = earNode.getNext();
......@@ -998,33 +992,52 @@ public interface ITriConnectivity<P extends IPoint, CE, CF, V extends IVertex<P>
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(prevEarNode);
heap.remove(nextEarNode);
heap.remove(nnextEarNode);
prevEarNode.getElement().setLast(earNode.getElement().getLast());
nextEarNode.getElement().setFirst(earNode.getElement().getFirst());
prevEarNode.getElement().setLast(t);
nextEarNode.getElement().setFirst(e1);
nextEarNode.getElement().setMiddle(t);
nnextEarNode.getElement().setFirst(t);
earNode.remove();
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> nnextEar = nnextEarNode.getElement();
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));
nnextEar.setPower(power(nnextEar.getEdges().get(0), nnextEar.getEdges().get(1), nnextEar.getEdges().get(2), vertex));
heap.add(prevEarNode);
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 p2 = getMesh().toPoint(getMesh().getPoint(e2));
VPoint p3 = getMesh().toPoint(getMesh().getPoint(e3));
VTriangle triangle = new VTriangle(p1, p2, p3);
VPoint x = triangle.getCircumcenter();
double r = triangle.getCircumscribedRadius();
double xp = new VLine(x, new VPoint(p)).length();
return xp * xp - r*r;
double xpSq = x.distanceSq(p);
double power = (xpSq - r*r);
return -power;
}
/**
......
......@@ -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) {
this.mesh = mesh;
this.edge = edge;
this.current = mesh.getNext(edge);
this.current = mesh.getTwin(edge);
this.first = true;
}
@Override
public boolean hasNext() {
return (first || current != mesh.getNext(edge));
return (first || current != mesh.getTwin(edge));
}
@Override
public E next() {
E result = current;
current = mesh.getNext(mesh.getTwin(result));
current = mesh.getTwin(mesh.getPrev(result));
first = false;
count++;
//log.info(count);
......
package org.vadere.geometry.mesh;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.vadere.meshing.mesh.gen.PFace;
import org.vadere.meshing.mesh.gen.PHalfEdge;
import org.vadere.meshing.mesh.gen.PVertex;
import org.vadere.meshing.mesh.impl.VPTriangulation;
import org.vadere.meshing.mesh.inter.IMesh;
import org.vadere.meshing.mesh.inter.IIncrementalTriangulation;
import org.vadere.meshing.mesh.triangulation.triangulator.impl.PDelaunayTriangulation;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VRectangle;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
/**
* @author Benedikt Zoennchen
......@@ -24,7 +26,6 @@ import static junit.framework.TestCase.assertTrue;
public class TestTriangulationOperations {
private VPTriangulation triangulation;
private IMesh<VPoint, Object, Object, PVertex<VPoint, Object, Object>, PHalfEdge<VPoint, Object, Object>, PFace<VPoint, Object, Object>> mesh;
private VPoint collapsePoint = new VPoint(0.5, 0);
private List<VPoint> points = new ArrayList<>();
private VRectangle bound = new VRectangle(-0.5, -0.5, 2.0, 2.0);
......@@ -38,11 +39,11 @@ public class TestTriangulationOperations {
points.add(new VPoint(0.5, 1));
triangulation.insert(points);
triangulation.finish();
mesh = triangulation.getMesh();
}
@Test
public void testCollapse() {
var mesh = triangulation.getMesh();
PVertex<VPoint, Object, Object> vertex = mesh
.streamVertices().filter(v -> mesh.getPoint(v).equals(collapsePoint))
.findAny().get();
......@@ -75,6 +76,7 @@ public class TestTriangulationOperations {
@Test
public void testIsValid() {
assertTrue(triangulation.getMesh().isValid());
assertTrue(triangulation.isValid());
}
......@@ -82,4 +84,43 @@ public class TestTriangulationOperations {
public void testRecompute() {
triangulation.recompute();
}
@Test
public void testRemovePoint() {
List<VPoint> points = Arrays.asList(new VPoint(0,0),
new VPoint(1, 0),
new VPoint(1, 1),
new VPoint(0, 1),
new VPoint(0.5, 0.5),
new VPoint(0.3, 0.8),
new VPoint(0.12, 0.23),
new VPoint(0.3, 0.3),
new VPoint(0.3, 0.6));
var delaunayTriangulation = new PDelaunayTriangulation<VPoint, Integer, Integer>(points, (x, y) -> new VPoint(x, y));
var triangulation = delaunayTriangulation.generate();
var mesh = delaunayTriangulation.getMesh();
assertEquals(points.size(), mesh.getNumberOfVertices());
triangulation.remove(new VPoint(0.5, 0.5));
assertEquals(points.size()-1, mesh.getNumberOfVertices());
Assert.assertTrue(mesh.isValid());
triangulation.remove(new VPoint(0.3, 0.3));
assertEquals(points.size()-2, mesh.getNumberOfVertices());
Assert.assertTrue(mesh.isValid());
triangulation.remove(new VPoint(0.3, 0.6));
assertEquals(points.size()-3, mesh.getNumberOfVertices());
Assert.assertTrue(mesh.isValid());
triangulation.insert(new VPoint(0.15, 0.5));
assertEquals(points.size()-2, mesh.getNumberOfVertices());
Assert.assertTrue(mesh.isValid());
triangulation.remove(new VPoint(0.15, 0.5));
assertEquals(points.size()-3, mesh.getNumberOfVertices());
Assert.assertTrue(mesh.isValid());
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment