diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PHalfEdge.java b/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PHalfEdge.java index 5fbb0128d1db3a47cc792852c7783be5cd3df54f..e7c44cad1d7d4041f475e45405002ddd686e3aff 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PHalfEdge.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PHalfEdge.java @@ -11,7 +11,7 @@ public class PHalfEdge

implements IHalfEdge

{ /** * point at the end of the half edge. */ - private P end; + private PVertex

end; /** * next half-edge around the face. @@ -35,12 +35,12 @@ public class PHalfEdge

implements IHalfEdge

{ private PFace

face; - PHalfEdge(@NotNull final P end, @NotNull final PFace

face) { + PHalfEdge(@NotNull final PVertex

end, @NotNull final PFace

face) { this.end = end; this.face = face; } - PHalfEdge(@NotNull final P end) { + PHalfEdge(@NotNull final PVertex

end) { this.end = end; this.face = null; } @@ -53,7 +53,7 @@ public class PHalfEdge

implements IHalfEdge

{ this.face = face; } - P getEnd() { + PVertex

getEnd() { return end; } @@ -116,7 +116,7 @@ public class PHalfEdge

implements IHalfEdge

{ } } - void setEnd(P end) { + void setEnd(PVertex

end) { this.end = end; } diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PMesh.java b/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PMesh.java index 386e47c88ee7f618c13b9b2ec21161efb52a3986..8c7d654034502808baea2bc346238d1cbe17610f 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PMesh.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PMesh.java @@ -11,17 +11,18 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Benedikt Zoennchen */ -public class PMesh

implements IMesh, PFace

> { +public class PMesh

implements IMesh, PHalfEdge

, PFace

> { private List> faces; private PFace

boundary; private List> edges; private IPointConstructor

pointConstructor; - private Set

vertices; + private Set> vertices; public PMesh(final IPointConstructor

pointConstructor) { this.faces = new ArrayList<>(); @@ -48,13 +49,13 @@ public class PMesh

implements IMesh, PFace

> } @Override - public PFace

getFace(final PHalfEdge

halfEdge) { + public PFace

getFace(@NotNull final PHalfEdge

halfEdge) { return halfEdge.getFace(); } @Override - public PHalfEdge

getEdge(@NotNull P vertex) { - return null; + public PHalfEdge

getEdge(@NotNull PVertex

vertex) { + return vertex.getEdge(); } @Override @@ -63,10 +64,20 @@ public class PMesh

implements IMesh, PFace

> } @Override - public P getVertex(@NotNull PHalfEdge

halfEdge) { + public P getPoint(@NotNull PHalfEdge

halfEdge) { + return getVertex(halfEdge).getPoint(); + } + + @Override + public PVertex

getVertex(@NotNull PHalfEdge

halfEdge) { return halfEdge.getEnd(); } + @Override + public P getPoint(@NotNull PVertex

vertex) { + return vertex.getPoint(); + } + @Override public PFace

getFace() { return faces.stream().filter(face -> !face.isDestroyed()).findAny().get(); @@ -118,22 +129,22 @@ public class PMesh

implements IMesh, PFace

> } @Override - public void setEdge(@NotNull P vertex, @NotNull PHalfEdge

edge) { - throw new UnsupportedOperationException("not jet implemented."); + public void setEdge(@NotNull PVertex

vertex, @NotNull PHalfEdge

edge) { + vertex.setEdge(edge); } @Override - public void setVertex(@NotNull PHalfEdge

halfEdge, @NotNull P vertex) { + public void setVertex(@NotNull PHalfEdge

halfEdge, @NotNull PVertex

vertex) { halfEdge.setEnd(vertex); } @Override - public List> getEdges(@NotNull P vertex) { + public List> getEdges(@NotNull final PVertex

vertex) { return edges.stream().filter(edge -> !edge.isValid()).filter(edge -> getVertex(edge).equals(vertex)).collect(Collectors.toList()); } @Override - public Collection

getVertices() { + public Collection> getVertices() { return vertices; } @@ -148,14 +159,14 @@ public class PMesh

implements IMesh, PFace

> } @Override - public PHalfEdge

createEdge(@NotNull P vertex) { + public PHalfEdge

createEdge(@NotNull PVertex

vertex) { PHalfEdge

edge = new PHalfEdge<>(vertex); edges.add(edge); return edge; } @Override - public PHalfEdge

createEdge(@NotNull P vertex, @NotNull PFace

face) { + public PHalfEdge

createEdge(@NotNull PVertex

vertex, @NotNull PFace

face) { PHalfEdge

edge = new PHalfEdge<>(vertex, face); edges.add(edge); return edge; @@ -179,10 +190,20 @@ public class PMesh

implements IMesh, PFace

> } @Override - public P createVertex(double x, double y) { - P vertex = pointConstructor.create(x, y); - //vertices.add(vertex); - return vertex; + public P createPoint(double x, double y) { + return pointConstructor.create(x, y); + } + + + // TODO: maybe remove insertVertex! + @Override + public PVertex

createVertex(double x, double y) { + return createVertex(pointConstructor.create(x, y)); + } + + @Override + public PVertex

createVertex(P point) { + return new PVertex<>(point); } @Override @@ -191,12 +212,12 @@ public class PMesh

implements IMesh, PFace

> } @Override - public void insert(P vertex) { + public void insert(final PVertex

vertex) { vertices.add(vertex); } @Override - public void insertVertex(P vertex) { + public void insertVertex(final PVertex

vertex) { vertices.add(vertex); } @@ -213,12 +234,32 @@ public class PMesh

implements IMesh, PFace

> } @Override - public void destroyVertex(@NotNull P vertex) { + public void setDown(@NotNull PVertex

up, @NotNull PVertex

down) { + up.setDown(down); + } + + @Override + public PVertex

getDown(@NotNull PVertex

vertex) { + return vertex.getDown(); + } + + @Override + public void destroyVertex(@NotNull PVertex

vertex) { vertices.remove(vertex); } + @Override + public Stream> streamFaces() { + return faces.stream(); + } + + @Override + public Stream> streamEdges() { + return streamFaces().flatMap(face -> streamEdges(face)); + } + @Override public List> getFaces() { - return faces.stream().filter(face -> !face.isDestroyed()).collect(Collectors.toList()); + return streamFaces().filter(face -> !face.isDestroyed()).collect(Collectors.toList()); } } diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PVertex.java b/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PVertex.java new file mode 100644 index 0000000000000000000000000000000000000000..ccc2cdd2bf1545faf2fd67358b9b1ac93d29a9c3 --- /dev/null +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/impl/PVertex.java @@ -0,0 +1,64 @@ +package org.vadere.util.geometry.mesh.impl; + +import org.vadere.util.geometry.mesh.inter.IVertex; +import org.vadere.util.geometry.shapes.IPoint; + +/** + * @author Benedikt Zoennchen + * @param

+ */ +public class PVertex

implements IVertex

{ + + private final P point; + private PVertex

down; + private PHalfEdge

halfEdge; + + public PVertex(final P point) { + this.point = point; + this.down = null; + } + + @Override + public P getPoint() { + return point; + } + + public PHalfEdge

getEdge() { + return halfEdge; + } + + public void setEdge(final PHalfEdge

halfEdge) { + this.halfEdge = halfEdge; + } + + public PVertex

getDown() { + return down; + } + + public void setDown(final PVertex

down) { + this.down = down; + } + + @Override + public boolean equals(Object obj) { + if(obj == null) { + return false; + } + + if(obj.getClass() != this.getClass()) { + return false; + } + + return point.equals(((PVertex

)obj).getPoint()); + } + + @Override + public int hashCode() { + return point.hashCode(); + } + + @Override + public String toString() { + return point.toString(); + } +} diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/inter/IPolyConnectivity.java b/VadereUtils/src/org/vadere/util/geometry/mesh/inter/IPolyConnectivity.java index acb10e2a0853306b9476618e4effbc89b490f50d..371c496e0c29e58d55f418a3b3de2387d127da28 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/inter/IPolyConnectivity.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/inter/IPolyConnectivity.java @@ -14,16 +14,30 @@ import java.util.Set; /** * @author Benedikt Zoennchen */ -public interface IPolyConnectivity

, F extends IFace

> extends Iterable{ +public interface IPolyConnectivity

, E extends IHalfEdge

, F extends IFace

> extends Iterable{ - IMesh getMesh(); + /** + * Returns the mesh of this IPolyConnectivity. + * Non mesh changing method. + * + * @return the mesh of this IPolyConnectivity + */ + IMesh getMesh(); - default boolean isAtBoundary(E halfEdge) { - IMesh mesh = getMesh(); + default boolean isAtBoundary(@NotNull final E halfEdge) { + IMesh mesh = getMesh(); return mesh.isBoundary(halfEdge) || mesh.isBoundary(mesh.getTwin(halfEdge)); } - default Optional locate(final double x, final double y) { + /** + * Searches and returns the face containing the point (x,y). + * Non mesh changing method. + * + * @param x x-coordinate of the location point + * @param y y-coordinate of the location point + * @return the face containing the point or empty() if there is none + */ + default Optional locateFace(final double x, final double y) { for(F face : getMesh().getFaces()) { VPolygon polygon = getMesh().toPolygon(face); if(polygon.contains(new VPoint(x, y))) { @@ -33,25 +47,65 @@ public interface IPolyConnectivity

, F e return Optional.empty(); } - default Optional locate(final P point) { - return locate(point.getX(), point.getY()); + /** + * Searches and returns the face containing the point (x,y). + * Non mesh changing method. + * + * @param point the location point + * @return the face containing the point or empty() if there is none + */ + default Optional locateFace(@NotNull final P point) { + return locateFace(point.getX(), point.getY()); } - default boolean isAtBoundary(F face) { + /** + * Tests if the face share any boundary edge. + * Non mesh changing method. + * + * @param face the face + * @return true if the face share any boundary edge, otherwise false + */ + default boolean isAtBoundary(@NotNull final F face) { return getMesh().getEdges(face).stream().anyMatch(edge -> isAtBoundary(edge)); } - default void adjustVertex(P vertex){ + /** + * If there is an half-edge e which is at the boundary and has the vertex v + * as its end point, this method will set the half-edge of v to e. + * Non mesh changing method. + * + * @param vertex v + */ + default void adjustVertex(@NotNull final V vertex) { List edges = getMesh().getEdges(vertex); edges.stream().filter(edge -> isAtBoundary(edge)).findAny().ifPresent(edge -> getMesh().setEdge(vertex, edge)); } - default Optional findEdge(P begin, P end) { - IMesh mesh = getMesh(); - return mesh.getIncidentEdges(mesh.getEdge(begin)).stream().filter(edge -> mesh.getPrev(edge).equals(end)).map(edge -> mesh.getTwin(edge)).findAny(); + /** + * Returns a half-edge (begin, end) where end is its end point + * and begin is the end point of its predecessor. + * Non mesh changing method. + * + * @param begin the end point of the predecessor of the searched half-edge + * @param end the end point of the searched half-edge + * @return a half-edge (begin, end) if there is any, otherwise empty() + */ + default Optional findEdge(@NotNull final V begin, @NotNull final V end) { + IMesh mesh = getMesh(); + return mesh.getIncidentEdges(mesh.getEdge(begin)).stream() + .filter(edge -> mesh.getPrev(edge).equals(end)) + .map(edge -> mesh.getTwin(edge)).findAny(); } - default boolean isSimpleLink(E halfEdge) { + /** + * Tests if the half-edge is the only link (of the face of the half-edge) + * between the face of the half-edge and the face of its twin. + * Non mesh changing method. + * + * @param halfEdge a half-edge to test + * @return true if the half-edge is a simple link, false otherwise + */ + default boolean isSimpleLink(@NotNull final E halfEdge) { E edge = halfEdge; E twin = getMesh().getTwin(halfEdge); F twinFace = getMesh().getFace(twin); @@ -67,7 +121,15 @@ public interface IPolyConnectivity

, F e return true; } - default boolean isSimpleConnected(F face) { + /** + * Tests if there is any face which shares more than one edge with the face + * we are checking. + * Non mesh changing method. + * + * @param face the face we are checking + * @return true if there is no face which shares more than one edge with this face, false otherwise + */ + default boolean isSimpleConnected(@NotNull final F face) { Set faceSet = new HashSet<>(); E edge = getMesh().getEdge(face); E next = getMesh().getNext(edge); @@ -85,7 +147,33 @@ public interface IPolyConnectivity

, F e return true; } - default void split(F face, P vertex) { + /** + * Splitting the face i.e. a polygon into as many faces as the face has edges. + * Assumption: the vertex is valid i.e. it is contained any face. + * Mesh changing method. + * + * @param vertex the vertex which spilts the face which triangleContains the vertex. It has to be contained any face. + */ + default void split(@NotNull final V vertex) { + Optional optFace = locateFace(getMesh().getPoint(vertex)); + if(!optFace.isPresent()) { + throw new IllegalArgumentException(vertex + " is not contained in any face. Therefore, no face found to split into faces."); + } else { + split(optFace.get(), vertex); + } + } + + /** + * Splitting the face i.e. a polygon into as many faces as the face has edges. + * Assumption: the vertex is valid i.e. it is contained in the face. + * Mesh changing method. + * + * @param face the face to be split into n faces, where n is the number of edges of the face + * @param vertex the vertex which spilts the face. It has to be contained in the face + */ + default void split(@NotNull final F face, @NotNull final V vertex) { + assert locateFace(getMesh().getPoint(vertex)).get().equals(face); + E hend = getMesh().getEdge(face); E hh = getMesh().getNext(hend); E hold = getMesh().createEdge(vertex); @@ -100,7 +188,10 @@ public interface IPolyConnectivity

, F e E hnext = getMesh().getNext(hh); F newFace = getMesh().createFace(); getMesh().setEdge(newFace, hh); + + // update the edge of the vertex such that the last new created edge will be its edge E hnew = getMesh().createEdge(vertex); + getMesh().setEdge(vertex, hnew); getMesh().setNext(hnew, hold); getMesh().setNext(hold, hh); @@ -123,13 +214,16 @@ public interface IPolyConnectivity

, F e } /** - * Removes a simple link. + * Removes a simple link. This will be done by merging two faces into one remaining face. + * Assumption: the edge is a simple link + * Mesh changing method. * - * @param edge - * @return + * @param edge the simple link + * @return the remaining face */ - default F removeEdge(E edge) { + default F removeEdge(@NotNull final E edge) { assert isSimpleLink(edge) && !getMesh().isDestroyed(edge); + E twin = getMesh().getTwin(edge); F delFace = getMesh().getFace(edge); F remFace = getMesh().getFace(twin); @@ -151,10 +245,12 @@ public interface IPolyConnectivity

, F e getMesh().setNext(prevEdge, nextTwin); getMesh().setNext(prevTwin, nextEdge); - /* adjust vertices, mb later - P eVertex = getMesh().getVertex(edge); - P tVertex = getMesh().getVertex(twin); - */ + // adjust vertices, mb later + V eVertex = getMesh().getVertex(edge); + V tVertex = getMesh().getVertex(twin); + + getMesh().setEdge(eVertex, prevTwin); + getMesh().setEdge(tVertex, prevEdge); if(getMesh().getEdge(remFace).equals(edge)) { getMesh().setEdge(remFace, prevTwin); @@ -174,13 +270,21 @@ public interface IPolyConnectivity

, F e return remFace; } - default void removeFace(@NotNull F face, boolean deleteIsolatedVertices) { + /** + * Removes a face from the mesh by removing all boundary edges of the face. + * If there are no boundary edges the face will be converted to be a part of the boundary + * itself i.e. a hole. + * Mesh changing method. + * + * @param face the face that will be removed from the mesh + * @param deleteIsolatedVertices true means that all vertices with degree <= 1 will be removed as well + */ + default void removeFace(@NotNull final F face, final boolean deleteIsolatedVertices) { assert !getMesh().isDestroyed(face); getMesh().destroyFace(face); List delEdges = new ArrayList<>(); - - List

vertices = new ArrayList<>(); + List vertices = new ArrayList<>(); F boundary = getMesh().createFace(true); @@ -195,7 +299,7 @@ public interface IPolyConnectivity

, F e if(!delEdges.isEmpty()) { E h0, h1, next0, next1, prev0, prev1; - P v0, v1; + V v0, v1; for(E delEdge : delEdges) { h0 = delEdge; @@ -208,18 +312,37 @@ public interface IPolyConnectivity

, F e next1 = getMesh().getNext(h1); prev1 = getMesh().getPrev(h1); - // adjust next and prev handles + // adjust next and prev half-edges getMesh().setNext(prev0, next1); getMesh().setNext(prev1, next0); + // TODO: test the isolated part! + boolean isolated0 = getMesh().getNext(prev1).equals(getMesh().getTwin(prev1)); + boolean isolated1 = getMesh().getNext(prev0).equals(getMesh().getTwin(prev0)); + + // adjust vertices + if(getMesh().getEdge(v0).equals(h0) && !isolated0) { + getMesh().setEdge(v0, prev1); + } + + if(deleteIsolatedVertices && isolated0) { + getMesh().destroyVertex(v0); + } + + if(getMesh().getEdge(v1).equals(h1) && !isolated1) { + getMesh().setEdge(v1, prev0); + } + + if(deleteIsolatedVertices && isolated1) { + getMesh().destroyVertex(v1); + } + // mark edge deleted if the mesh has a edge status getMesh().destroyEdge(h0); getMesh().destroyEdge(h1); - // TODO: delete isolated vertices? - - - for(P vertex : vertices) { + // TODO: do we need this? + for(V vertex : vertices) { adjustVertex(vertex); } } diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/inter/ITriConnectivity.java b/VadereUtils/src/org/vadere/util/geometry/mesh/inter/ITriConnectivity.java index be9bf58db1528d0293bc4ee6da00d268a67b1ad9..32792ca8d1176f02d9761eaee19db1c4ee17894b 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/inter/ITriConnectivity.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/inter/ITriConnectivity.java @@ -1,5 +1,7 @@ package org.vadere.util.geometry.mesh.inter; +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.shapes.IPoint; @@ -7,11 +9,9 @@ import org.vadere.util.geometry.shapes.VLine; import org.vadere.util.geometry.shapes.VPoint; import org.vadere.util.geometry.shapes.VTriangle; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.Random; /** * @author Benedikt Zoennchen @@ -20,26 +20,60 @@ import java.util.Optional; * @param * @param */ -public interface ITriConnectivity

, F extends IFace

> extends IPolyConnectivity { +public interface ITriConnectivity

, E extends IHalfEdge

, F extends IFace

> extends IPolyConnectivity { - default void splitFaceEvent(F original, F... faces) {} + Logger log = LogManager.getLogger(ITriConnectivity.class); + Random random = new Random(); + /** + * Non mesh changing method. + * + * @param original + * @param f1 + * @param f2 + * @param f3 + */ + default void splitTriangleEvent(F original, F f1, F f2, F f3) {} + + default void splitEdgeEvent(F original, F f1, F f2) {} + + /** + * Non mesh changing method. + * + * @param f1 + * @param f2 + */ default void flipEdgeEvent(F f1, F f2) {} + /** + * Non mesh changing method. + * + * @param vertex + */ default void insertEvent(E vertex) {}; - boolean isIllegal(E edge); + /** + * Non mesh changing method. + * + * @param edge + * @return + */ + boolean isIllegal(E edge, V p); /** * Splits the half-edge at point p, preserving a valid triangulation. + * Assumption: p is located on the edge! + * Mesh changing method. * * @param p the split point * @param halfEdge the half-edge which will be split + * @return an newly created half-edge which has p as its end-point */ - default List splitEdge(@NotNull P p, @NotNull E halfEdge, boolean legalize) { - IMesh mesh = getMesh(); - mesh.insertVertex(p); - List newEdges = new ArrayList<>(4); + default E splitEdge(@NotNull P p, @NotNull E halfEdge, boolean legalize) { + IMesh mesh = getMesh(); + V v = mesh.createVertex(p); + mesh.insertVertex(v); + /* * Situation: h0 = halfEdge * h1 -> h2 -> h0 @@ -65,18 +99,19 @@ public interface ITriConnectivity

, F ex E h0 = halfEdge; E o0 = mesh.getTwin(h0); - P v2 = mesh.getVertex(o0); + V v2 = mesh.getVertex(o0); F f0 = mesh.getFace(h0); F f3 = mesh.getFace(o0); // faces correct? mesh.createEdge(v2, mesh.getFace(o0)); E e1 = mesh.createEdge(v2, mesh.getFace(o0)); - E t1 = mesh.createEdge(p, mesh.getFace(h0)); + + E t1 = mesh.createEdge(v, mesh.getFace(h0)); + mesh.setEdge(v, t1); + mesh.setTwin(e1, t1); - mesh.setVertex(o0, p); - newEdges.add(t1); - newEdges.add(h0); + mesh.setVertex(o0, v); if(!mesh.isBoundary(h0)) { F f1 = mesh.createFace(); @@ -84,12 +119,11 @@ public interface ITriConnectivity

, F ex E h1 = mesh.getNext(h0); E h2 = mesh.getNext(h1); - P v1 = mesh.getVertex(h1); + V v1 = mesh.getVertex(h1); E e0 = mesh.createEdge(v1, f1); - E t0 = mesh.createEdge(p, f0); + E t0 = mesh.createEdge(v, f0); mesh.setTwin(e0, t0); - newEdges.add(t0); mesh.setEdge(f0, h0); mesh.setEdge(f1, h2); @@ -110,7 +144,7 @@ public interface ITriConnectivity

, F ex mesh.setNext(h2, t1); mesh.setNext(t1, e0); - splitFaceEvent(f0, f0, f1); + splitEdgeEvent(f0, f0, f1); } else { mesh.setNext(mesh.getPrev(h0), t1); @@ -121,14 +155,13 @@ public interface ITriConnectivity

, F ex E o1 = mesh.getNext(o0); E o2 = mesh.getNext(o1); - P v3 = mesh.getVertex(o1); + V v3 = mesh.getVertex(o1); F f2 = mesh.createFace(); // face E e2 = mesh.createEdge(v3, mesh.getFace(o0)); - E t2 = mesh.createEdge(p, f2); + E t2 = mesh.createEdge(v, f2); mesh.setTwin(e2, t2); - newEdges.add(t2); mesh.setEdge(f2, o1); mesh.setEdge(f3, o0); @@ -149,7 +182,7 @@ public interface ITriConnectivity

, F ex mesh.setNext(e2, o2); mesh.setNext(o2, o0); - splitFaceEvent(f3, f3, f2); + splitEdgeEvent(f3, f3, f2); } else { mesh.setNext(e1, mesh.getNext(o0)); @@ -160,33 +193,34 @@ public interface ITriConnectivity

, F ex if(!mesh.isBoundary(h0)) { E h1 = mesh.getNext(h0); E h2 = mesh.getPrev(t1); - legalize(h1); - legalize(h2); + legalize(h1, v); + legalize(h2, v); } if(!mesh.isBoundary(o0)) { E o1 = mesh.getNext(e1); E o2 = mesh.getPrev(o0); - legalize(o1); - legalize(o2); + legalize(o1, v); + legalize(o2, v); } } - return newEdges; + return t1; } - default List splitEdge(@NotNull P p, @NotNull E halfEdge) { + default E splitEdge(@NotNull P p, @NotNull E halfEdge) { return splitEdge(p, halfEdge, true); } /** * Flips an edge in the triangulation assuming the egdge which will be * created is not jet there. + * mesh changing method. * * @param edge the edge which will be flipped. */ default void flip(@NotNull final E edge) { - IMesh mesh = getMesh(); + IMesh mesh = getMesh(); // 1. gather all the references required E a0 = edge; @@ -200,6 +234,12 @@ public interface ITriConnectivity

, F ex F fa = mesh.getFace(a0); F fb = mesh.getFace(b0); + V va1 = mesh.getVertex(a1); + V vb1 = mesh.getVertex(b1); + + V va0 = mesh.getVertex(a0); + V vb0 = mesh.getVertex(b0); + if(mesh.getEdge(fb).equals(b1)) { mesh.setEdge(fb, a1); } @@ -208,8 +248,17 @@ public interface ITriConnectivity

, F ex mesh.setEdge(fa, b1); } - mesh.setVertex(a0, mesh.getVertex(a1)); - mesh.setVertex(b0, mesh.getVertex(b1)); + // TODO: maybe without if, just do it? its faster? + if(mesh.getEdge(va0).equals(a0)) { + mesh.setEdge(va0, b2); + } + + if(mesh.getEdge(vb0).equals(b0)) { + mesh.setEdge(vb0, a2); + } + + mesh.setVertex(a0, va1); + mesh.setVertex(b0, vb1); mesh.setNext(a0, a2); mesh.setNext(a2, b1); @@ -226,43 +275,48 @@ public interface ITriConnectivity

, F ex } default boolean isTriangle(F face) { - IMesh mesh = getMesh(); + IMesh mesh = getMesh(); List edges = mesh.getEdges(face); return edges.size() == 3; } - default boolean insert(@NotNull F face, @NotNull P p) { - throw new UnsupportedOperationException("not jet implemented."); - } + E insert(@NotNull P p, @NotNull F face); /** * Splits the triangle xyz into three new triangles xyp, yzp and zxp. + * Assumption: p is inside the face. + * Mesh changing method. * - * @param p the point which splits the triangle - * @param face the triangle face we split + * @param point the point which splits the triangle + * @param face the triangle face we split + * @param legalize true means that recursive filps will be done afterwards to legalize illegal edges (e.g. delaunay requirement). * - * returns a list of all newly created face. + * @return an half-edge which has point as its end-point */ - default List splitTriangle(@NotNull F face, P p, boolean legalize) { - assert isTriangle(face); + default E splitTriangle(@NotNull F face, @NotNull final P point, boolean legalize) { + //assert isTriangle(face) && locateFace(point).get().equals(face); + + V p = getMesh().createVertex(point); getMesh().insertVertex(p); - List faceList = new ArrayList<>(3); - IMesh mesh = getMesh(); - List edges = mesh.getEdges(face); + IMesh mesh = getMesh(); F xyp = mesh.createFace(); F yzp = mesh.createFace(); - F zxp = mesh.createFace(); - E zx = edges.get(0); - E xy = edges.get(1); - E yz = edges.get(2); + //F zxp = mesh.createFace(); + F zxp = face; - P x = mesh.getVertex(zx); - P y = mesh.getVertex(xy); - P z = mesh.getVertex(yz); + E zx = mesh.getEdge(face); + E xy = mesh.getNext(zx); + E yz = mesh.getNext(xy); + + V x = mesh.getVertex(zx); + V y = mesh.getVertex(xy); + V z = mesh.getVertex(yz); E yp = mesh.createEdge(p, xyp); + mesh.setEdge(p, yp); + E py = mesh.createEdge(y, yzp); mesh.setTwin(yp, py); @@ -294,70 +348,86 @@ public interface ITriConnectivity

, F ex mesh.setFace(zx, zxp); mesh.setFace(yz, yzp); - // TODO: maybe we do not destroy the face, instead use it. - mesh.destroyFace(face); - faceList.add(xyp); - faceList.add(yzp); - faceList.add(zxp); + // we reuse the face for efficiency + //mesh.destroyFace(face); - splitFaceEvent(face, xyp, yzp, zxp); + splitTriangleEvent(face, xyp, yzp, zxp); if(legalize) { - legalize(zx); - legalize(xy); - legalize(yz); + legalize(zx, p); + legalize(xy, p); + legalize(yz, p); } - return faceList; + return xp; } - default List splitTriangle(@NotNull F face, P p) { + /** + * Splits the triangle xyz into three new triangles xyp, yzp and zxp and legalizes all possibly illegal edges. + * Assumption: p is inside the face. + * Mesh changing method. + * + * @param p the point which splits the triangle + * @param face the triangle face we split + * + * returns a list of all newly created face. + */ + default E splitTriangle(@NotNull final F face, @NotNull final P p) { return splitTriangle(face, p, true); } /** * Legalizes an edge xy of a triangle xyz if it is illegal by flipping it. + * Mesh changing method. * * @param edge an edge zx of a triangle xyz */ - default void legalize(E edge) { - if(isIllegal(edge)) { + /*default void legalize(@NotNull final E edge, final V p, int depth) { + if(isIllegal(edge, p)) { assert isFlipOk(edge); - - P p = getMesh().getVertex(getMesh().getNext(edge)); + assert getMesh().getVertex(getMesh().getNext(edge)).equals(p); F f1 = getMesh().getFace(edge); F f2 = getMesh().getTwinFace(edge); flip(edge); - P vertex = getMesh().getVertex(edge); + V vertex = getMesh().getVertex(edge); if(vertex.equals(p)) { E e1 = getMesh().getPrev(edge); - E e2 = getMesh().getPrev(getMesh().getTwin(edge)); - legalize(e1); - legalize(getMesh().getPrev(e2)); + E e2 = getMesh().getNext(getMesh().getTwin(edge)); + + + legalize(e1, p, depth+1); + legalize(e2, p, depth+1); } else { E e1 = getMesh().getNext(edge); - E e2 = getMesh().getNext(getMesh().getTwin(edge)); - legalize(e1); - legalize(getMesh().getNext(e2)); + E e2 = getMesh().getPrev(getMesh().getTwin(edge)); + legalize(e1, p, depth+1); + legalize(e2, p, depth+1); } } + }*/ + + void legalizeNonRecursive(final E edge, final V p); + + default void legalize(final E edge, V p) { + legalizeNonRecursive(edge, p); } /** * Tests if a flip for this half-edge is valid, i.e. the edge does not already exist. + * Non mesh changing method. * * @param halfEdge the half-edge that might be flipped * @return true if and only if the flip is valid */ - default boolean isFlipOk(final E halfEdge) { + default boolean isFlipOk(@NotNull final E halfEdge) { if(getMesh().isBoundary(halfEdge)) { return false; } @@ -369,7 +439,7 @@ public interface ITriConnectivity

, F ex return false; } - P vertex = getMesh().getVertex(getMesh().getNext(yx)); + V vertex = getMesh().getVertex(getMesh().getNext(yx)); for(E neigbhour : getMesh().getIncidentEdgesIt(getMesh().getNext(xy))) { if(getMesh().getVertex(neigbhour).equals(vertex)) { @@ -381,14 +451,17 @@ public interface ITriConnectivity

, F ex } @Override - default Optional locate(final P point) { - return this.locate(point.getX(), point.getY()); + default Optional locateFace(final P point) { + return this.locateFace(point.getX(), point.getY()); } @Override - default Optional locate(final double x, final double y) { - if(getMesh().getNumberOfFaces() > 0) { - return locate(x, y, getMesh().getFace()); + default Optional locateFace(final double x, final double y) { + if(getMesh().getNumberOfFaces() > 1) { + return locateFace(x, y, getMesh().getFace()); + } + else if(getMesh().getNumberOfFaces() == 1) { + return Optional.of(getMesh().getFace()); } else { return Optional.empty(); @@ -396,7 +469,7 @@ public interface ITriConnectivity

, F ex } /*default Optional

locateVertex(double x, double y, F startFace) { - Optional optFace = locate(x, y, startFace); + Optional optFace = locateFace(x, y, startFace); if(optFace.isPresent()) { F face = optFace.get(); @@ -409,7 +482,15 @@ public interface ITriConnectivity

, F ex return Optional.empty(); }*/ - default Optional locate(double x, double y, F startFace) { + /** + * None mesh changing method. + * + * @param x + * @param y + * @param startFace + * @return + */ + default Optional locateFace(double x, double y, F startFace) { // there is no face. if(getDimension() <= 0 ){ return Optional.empty(); @@ -419,12 +500,22 @@ public interface ITriConnectivity

, F ex return marchLocate1D(x, y, startFace); } else { - VTriangle triangle = getMesh().toTriangle(startFace); - VPoint incenter = triangle.getIncenter(); - return Optional.of(marchLocate2D(incenter.getX(), incenter.getY(), x, y, startFace)); + return Optional.of(marchRandom2D(x, y, startFace)); } } + default Optional locateFace(P point, F startFace) { + return locateFace(point.getX(), point.getY(), startFace); + } + + /** + * None mesh changing method. + * + * @param x + * @param y + * @param startFace + * @return + */ default Optional marchLocate1D(double x, double y, F startFace) { if(contains(startFace, x, y)) { return Optional.of(startFace); @@ -435,31 +526,38 @@ public interface ITriConnectivity

, F ex } /** - * Marching to the face which contains the point defined by (x2, y2). The march - * starts at (x1, y2) at inside the startFace. + * Marching to the face which triangleContains the point defined by (x2, y2). Inside the startFace. + * None mesh changing method. * - * @param x1 the x-coordinate of the starting point - * @param y1 the y-coordinate of the starting point - * @param x2 the x-coordinate of the ending point - * @param y2 the y-coordinate of the ending point + * @param x1 the x-coordinate of the ending point + * @param y1 the y-coordinate of the ending point * @param startFace the face where the march start containing (x1,y1). * @return */ - default F marchLocate2D(double x1, double y1, double x2, double y2, F startFace) { - assert !getMesh().isBoundary(startFace); - assert contains(startFace, x1, y1); + default F marchLocate2D(double x1, double y1, F startFace) { + //assert !getMesh().isBoundary(startFace); + //assert contains(startFace, x1, y1); + + VTriangle triangle = getMesh().toTriangle(startFace); + VPoint incenter = triangle.getIncenter(); + + double x2 = incenter.getX(); + double y2 = incenter.getY(); E entryEdge = null; F face = startFace; boolean found = true; - while(found && !contains(face, x2, y2)) { + //TODO: this seems to be very slow! + + while(found && !contains(face, x1, y1)) { found = false; for(E halfEdge : getMesh().getEdgeIt(face)) { if(!halfEdge.equals(entryEdge)) { - P sVertex = getMesh().getVertex(getMesh().getPrev(halfEdge)); - P eVertex = getMesh().getVertex(halfEdge); + V sVertex = getMesh().getVertex(getMesh().getPrev(halfEdge)); + V eVertex = getMesh().getVertex(halfEdge); + //TODO: use faster/own implementation? if(VLine.linesIntersect(sVertex.getX(), sVertex.getY(), eVertex.getX(), eVertex.getY(), x1, y1, x2, y2)) { entryEdge = getMesh().getTwin(halfEdge); face = getMesh().getTwinFace(halfEdge); @@ -470,15 +568,242 @@ public interface ITriConnectivity

, F ex } } - assert found || getMesh().isBoundary(face); + //assert found || getMesh().isBoundary(face); return face; } - default boolean contains(F face, double x, double y) { + default F marchRandom2D(double x1, double y1, F startFace) { + boolean first = true; + F face = startFace; + F prevFace = null; + int count = 0; + + while (true) { + count++; + //boolean goLeft = random.nextBoolean(); + boolean goLeft = true; + + E e1 = getMesh().getEdge(face); + E e2 = getMesh().getNext(e1); + E e3 = getMesh().getNext(e2); + + V v1 = getMesh().getVertex(e1); + V v2 = getMesh().getVertex(e2); + V v3 = getMesh().getVertex(e3); + + // loop unrolling for efficiency! + if(first) { + first = false; + prevFace = face; + + if (GeometryUtils.isRightOf(v3, v1, x1, y1)) { + face = getMesh().getTwinFace(e1); + continue; + } + + if (GeometryUtils.isRightOf(v1, v2, x1, y1)) { + face = getMesh().getTwinFace(e2); + continue; + } + + if (GeometryUtils.isRightOf(v2, v3, x1, y1)) { + face = getMesh().getTwinFace(e3); + continue; + } + } else if(goLeft) { + if(prevFace == getMesh().getTwinFace(e1)) { + prevFace = face; + + if (GeometryUtils.isRightOf(v2, v3, x1, y1)) { + face = getMesh().getTwinFace(e3); + continue; + } + + if(GeometryUtils.isRightOf(v1, v2, x1, y1)) { + face = getMesh().getTwinFace(e2); + continue; + } + + } + else if(prevFace == getMesh().getTwinFace(e2)) { + prevFace = face; + + if (GeometryUtils.isRightOf(v3, v1, x1, y1)) { + face = getMesh().getTwinFace(e1); + continue; + } + + if (GeometryUtils.isRightOf(v2, v3, x1, y1)) { + face = getMesh().getTwinFace(e3); + continue; + } + + } + else { + prevFace = face; + + if(GeometryUtils.isRightOf(v1, v2, x1, y1)) { + face = getMesh().getTwinFace(e2); + continue; + } + + if (GeometryUtils.isRightOf(v3, v1, x1, y1)) { + face = getMesh().getTwinFace(e1); + continue; + } + + } + } + else { + if(prevFace == getMesh().getTwinFace(e1)) { + prevFace = face; + + if(GeometryUtils.isRightOf(v1, v2, x1, y1)) { + face = getMesh().getTwinFace(e2); + continue; + } + + if (GeometryUtils.isRightOf(v2, v3, x1, y1)) { + face = getMesh().getTwinFace(e3); + continue; + } + + } + else if(prevFace == getMesh().getTwinFace(e2)) { + prevFace = face; + + if (GeometryUtils.isRightOf(v2, v3, x1, y1)) { + face = getMesh().getTwinFace(e3); + continue; + } + + if (GeometryUtils.isRightOf(v3, v1, x1, y1)) { + face = getMesh().getTwinFace(e1); + continue; + } + + } + else { + prevFace = face; + + if (GeometryUtils.isRightOf(v3, v1, x1, y1)) { + face = getMesh().getTwinFace(e1); + continue; + } + + if(GeometryUtils.isRightOf(v1, v2, x1, y1)) { + face = getMesh().getTwinFace(e2); + continue; + } + + } + } + //log.info("#traversed triangles = " + count); + return face; + } + } + + /** + * Returns the point which is closest to the p = (x1, y1) assuming it is in the + * circumcircle defined by the face. + * + * @param x1 the x-coordinate of the starting point + * @param y1 the y-coordinate of the starting point + * @param face the face where the march start containing (x1,y1). + * @return + */ + default V locateNearestNeighbour(double x1, double y1, F face) { + assert isInsideCircumscribedCycle(face, x1, y1); + + V nn = getNearestPoint(face, x1, y1); + E edge = getMesh().getEdge(face); + + + nn = lookUpNearestNeighbour(x1, y1, nn, getMesh().getTwin(edge)); + nn = lookUpNearestNeighbour(x1, y1, nn, getMesh().getTwin(getMesh().getNext(edge))); + nn = lookUpNearestNeighbour(x1, y1, nn, getMesh().getTwin(getMesh().getNext(edge))); + + return nn; + } + + default V locateNearestNeighbour(P point, F startFace) { + return locateNearestNeighbour(point.getX(), point.getY(), startFace); + } + + default V locateNearestNeighbour(P point) { + return locateNearestNeighbour(point.getX(), point.getY(), getMesh().getFace()); + } + + default V locateNearestNeighbour(double x1, double y1) { + return locateNearestNeighbour(x1, y1, getMesh().getFace()); + } + + //TODO: rename + default V lookUpNearestNeighbour(double x1, double y1, V nn, E edge) { + F face = getMesh().getFace(edge); + + if(isInsideCircumscribedCycle(face, x1, y1)) { + V v = getMesh().getVertex(edge); + + if(v.distance(x1, y1) < nn.distance(x1, y1)) { + nn = v; + } + + nn = lookUpNearestNeighbour(x1, y1, nn, getMesh().getTwin(getMesh().getNext(edge))); + nn = lookUpNearestNeighbour(x1, y1, nn, getMesh().getTwin(getMesh().getPrev(edge))); + } + + return nn; + } + + /** + * Tests if the point p = (x1, y1) is inside the circumscribedcycle defined by the triangle of the face. + * @param face the face + * @param x1 x-coordinate of the point + * @param y1 y-coordinate of the point + * @return true if the point p = (x1, y1) is inside the circumscribedcycle defined by the triangle of the face, false otherwise. + */ + default boolean isInsideCircumscribedCycle(final F face, double x1, double y1) { + E e1 = getMesh().getEdge(face); + E e2 = getMesh().getNext(e1); + E e3 = getMesh().getNext(e2); + + V v1 = getMesh().getVertex(e1); + V v2 = getMesh().getVertex(e2); + V v3 = getMesh().getVertex(e3); + + return GeometryUtils.isInsideCircle(v1, v2, v3, x1, y1); + } + + /** + * Returns vertex of the triangulation of the face with the smallest distance to point. + * Assumption: The face has to be part of the mesh of the triangulation. + * + * @param face the face of the trianuglation + * @param point the point + * @return vertex of the triangulation of the face with the smallest distance to point + */ + default V getNearestPoint(final F face, final P point) { + return getNearestPoint(face, point.getX(), point.getY()); + } + + default V getNearestPoint(final F face, final double x, final double y) { + IMesh mesh = getMesh(); + return mesh.streamEdges(face).map(edge -> mesh.getVertex(edge)).reduce((p1, p2) -> p1.distance(x,y) > p2.distance(x,y) ? p2 : p1).get(); + } - if(getMesh().isMember(face, x, y)) { + /** + * None mesh changing method. + * + * @param face + * @param x + * @param y + * @return + */ + default boolean contains(F face, double x, double y) { + if(isMember(face, x, y)) { return true; } @@ -487,29 +812,47 @@ public interface ITriConnectivity

, F ex return getMesh().toPolygon(face).contains(x, y); } else { - return getMesh().toTriangle(face).contains(x, y); + E h1 = getMesh().getEdge(face); + E h2 = getMesh().getNext(h1); + E h3 = getMesh().getNext(h2); + return GeometryUtils.triangleContains(getMesh().getPoint(h1), getMesh().getPoint(h2), getMesh().getPoint(h3), new VPoint(x, y)); } } //Optional marchLocate2DLFC(double x, double y, F startFace); - default P getMaxDistanceVertex(F face, double x, double y) { - List

vertices = getMesh().getVertices(face); + /** + * None mesh changing method. + * + * @param face + * @param x + * @param y + * @return + */ + default V getMaxDistanceVertex(F face, double x, double y) { + List vertices = getMesh().getVertices(face); - assert vertices.size() == 3; + //assert vertices.size() == 3; - P result = vertices.get(0); + V result = vertices.get(0); result = result.distance(x, y) < vertices.get(1).distance(x, y) ? vertices.get(1) : result; result = result.distance(x, y) < vertices.get(2).distance(x, y) ? vertices.get(2) : result; return result; } - default P getMinDistanceVertex(F face, double x, double y) { - List

vertices = getMesh().getVertices(face); + /** + * None mesh changing method. + * @param face + * @param x + * @param y + * @return + */ + default V getMinDistanceVertex(F face, double x, double y) { + List vertices = getMesh().getVertices(face); - assert vertices.size() == 3; + //assert vertices.size() == 3; - P result = vertices.get(0); + V result = vertices.get(0); result = result.distance(x, y) > vertices.get(1).distance(x, y) ? vertices.get(1) : result; result = result.distance(x, y) > vertices.get(2).distance(x, y) ? vertices.get(2) : result; return result; @@ -520,50 +863,142 @@ public interface ITriConnectivity

, F ex } /** - * Returns a Collection of faces which contain the point (x,y). In case of the point lying on the - * boundary of the face, the face contains the point. If the point lies on a vertex of the face, - * the face contains the point as well. + * Returns the closest half-edge of a face containing p = (x,y) if there is any face that contains p, otherwise empty(). + * Three cases are possible: + * 1) p is in the interior of the face + * 2) p lies on the edge which will be returned. + * 3) p is a vertex of the mesh * - * @param x the x-coordinate of the point - * @param y the y-coordinate of the point - * @return a Collection of faces which contain the point. + * @param x x-coordinate of the point p + * @param y y-coordinate of the point p + * @return the closest half-edge of a face containing p = (x,y) if there is any face that contains p, otherwise empty(). */ - default Collection getAdjacentFaces(final double x, final double y) { - Optional optFace = locate(x, y); + default Optional getClosestEdge(final double x, final double y) { + Optional optFace = locateFace(x, y); + if(optFace.isPresent()) { - F face = optFace.get(); + return Optional.of(getMesh().closestEdge(optFace.get(), x, y)); + } + else { + return Optional.empty(); + } + } - /* - * the point (x,y) may - * 1. lies on a vertex of the face, - * 2. lies on a edge of the face, - * 3. lies on the interior of the face - */ - Optional optMemberEdge = getMesh().getMemberEdge(face, x, y); - if(optMemberEdge.isPresent()) { - return getMesh().getAdjacentFaces(optMemberEdge.get()); - } - else { - List faces = new ArrayList<>(2); - faces.add(face); + /** + * Returns the closest half-edge of a face containing p = (x,y) if there is any face that contains p, otherwise empty(). + * Two cases are possible: + * 1) p is in the interior of the face + * 2) p lies on the edge which will be returned. + * 3) p is a vertex of the mesh + * + * @param x x-coordinate of the point p + * @param y y-coordinate of the point p + * @param startFace the face the search will start from + * @return the closest half-edge of a face containing p = (x,y) if there is any face that contains p, otherwise empty(). + */ + default Optional getClosestEdge(final double x, final double y, final F startFace) { + Optional optFace = locateFace(x, y, startFace); - for(E halfEdge : getMesh().getEdgeIt(face)) { - P v1 = getMesh().getVertex(halfEdge); - P v2 = getMesh().getVertex(getMesh().getPrev(halfEdge)); + if(optFace.isPresent()) { + return Optional.of(getMesh().closestEdge(optFace.get(), x, y)); + } + else { + return Optional.empty(); + } + } - // TODO: think about the epsilon, absolute value seems to be a really bad idea! - if(!getMesh().isBoundary(getMesh().getTwinFace(halfEdge)) && Math.abs(GeometryUtils.ccw(x, y, v1.getX(), v1.getY(), v2.getX(), v2.getY())) == 0.0) { - faces.add(getMesh().getTwinFace(halfEdge)); - break; - } - } + /** + * Returns the closest vertex of a face containing p = (x,y) if there is any face that contains p, otherwise empty(). + * Note that this might not be the closest point with respect to p in general. There might be a point closer which + * is however not part of the triangle which contains p. + * + * @param x x-coordinate of the point p + * @param y y-coordinate of the point p + * @return the closest vertex of a face containing p = (x,y) if there is any face that contains p, otherwise empty(). + */ + default Optional getClosestVertex(final double x, final double y) { + Optional optFace = locateFace(x, y); - return faces; - } + if(optFace.isPresent()) { + return Optional.of(getMesh().closestVertex(optFace.get(), x, y)); + } + else { + return Optional.empty(); + } + } + /** + * Returns the closest vertex of a face containing p = (x,y) if there is any face that contains p, otherwise empty(). + * Note that this might not be the closest point with respect to p in general. There might be a point closer which + * is however not part of the triangle which contains p. The search for the face containing p starts at the startFace. + * + * @param x x-coordinate of the point p + * @param y y-coordinate of the point p + * @param startFace the face the search will start from + * @return the closest vertex of a face containing p = (x,y) if there is any face that contains p, otherwise empty(). + */ + default Optional getClosestVertex(final double x, final double y, final F startFace) { + Optional optFace = locateFace(x, y, startFace); + + if(optFace.isPresent()) { + return Optional.of(getMesh().closestVertex(optFace.get(), x, y)); } else { - return Collections.emptyList(); + return Optional.empty(); + } + } + + default boolean isMember(F face, double x, double y, double epsilon) { + return getMemberEdge(face, x, y, epsilon).isPresent(); + } + + default Optional getMemberEdge(F face, double x, double y, double epsilon) { + E e1 = getMesh().getEdge(face); + E e2 = getMesh().getNext(e1); + E e3 = getMesh().getNext(e2); + + if(getMesh().getPoint(e1).distance(x, y) < epsilon) { + return Optional.of(e1); + } + + if(getMesh().getPoint(e2).distance(x, y) < epsilon) { + return Optional.of(e2); + } + + if(getMesh().getPoint(e3).distance(x, y) < epsilon) { + return Optional.of(e3); + } + + return Optional.empty(); + } + + default boolean isMember(F face, double x, double y) { + return getMemberEdge(face, x, y).isPresent(); + } + + default Optional getMemberEdge(F face, double x, double y) { + E e1 = getMesh().getEdge(face); + P p1 = getMesh().getPoint(e1); + + if(p1.getX() == x && p1.getY() == y) { + return Optional.of(e1); + } + + E e2 = getMesh().getNext(e1); + P p2 = getMesh().getPoint(e2); + + if(p2.getX() == x && p2.getY() == y) { + return Optional.of(e2); + } + + E e3 = getMesh().getNext(e2); + P p3 = getMesh().getPoint(e3); + + if(p3.getX() == x && p3.getY() == y) { + return Optional.of(e3); } + + return Optional.empty(); } + } diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/inter/ITriangulationHierarchy.java b/VadereUtils/src/org/vadere/util/geometry/mesh/inter/ITriangulationHierarchy.java deleted file mode 100644 index eace286face55de9180556c366065d3ccc0f211a..0000000000000000000000000000000000000000 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/inter/ITriangulationHierarchy.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.vadere.util.geometry.mesh.inter; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by bzoennchen on 04.04.17. - */ -public interface ITriangulationHierarchy< - P extends IHierarchyPoint, - E extends IHalfEdge

, - F extends IFace

, - T extends ITriangulation> { - - enum LOCATE_TYPE {} - - - default P insert(final double x, final double y, LOCATE_TYPE locateType, F face, int li) { - - int vertexLevel = randomLevel(); - List faces = locateInAll(x ,y); - - ITriangulation triangulation = getLevel(0); - P vertex = triangulation.getMesh().insertVertex(x, y); - triangulation.insert(faces.get(0), vertex); - - P prev = vertex; - P first = vertex; - - int level = 1; - while(level <= vertexLevel) { - triangulation = getLevel(level); - vertex = triangulation.getMesh().insertVertex(x, y); - vertex.setDown(prev);// link with level above - prev.setUp(vertex); - prev = vertex; - level++; - } - - return first; - } - - int randomLevel(); - - int getMaxLevel(); - - int getMinSize(); - - IHierarchyPoint create(P vertex); - - - ITriangulation getLevel(int level); - - default List locateInAll(double x, double y) { - int level = getMaxLevel(); - F face; - List faces = new ArrayList<>(getMaxLevel()); - - while(level > 0 && (getLevel(level).getMesh().getNumberOfVertices() < getMinSize() || getLevel(level).getDimension() < 2)) { - level--; - } - - for(int i = level+1; i < getMaxLevel(); i++) { - //pos[i] = 0??? - } - - while(level > 0) { - //getLevel(level).lo - //faces.add(face); - } - return null; - } -} diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/iterators/EdgeIterator.java b/VadereUtils/src/org/vadere/util/geometry/mesh/iterators/EdgeIterator.java index 9ade16d065fdb44bfd9acd14327b289ef98a4b64..3251ea36650d9fb8849c8f4a2be78aa3387fe3da 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/iterators/EdgeIterator.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/iterators/EdgeIterator.java @@ -3,6 +3,7 @@ package org.vadere.util.geometry.mesh.iterators; 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.IVertex; import org.vadere.util.geometry.shapes.IPoint; import java.util.Iterator; @@ -15,13 +16,13 @@ import java.util.Iterator; * @param the type of the half-edge * @param the type of the face */ -public class EdgeIterator

, F extends IFace

> implements Iterator { +public class EdgeIterator

, E extends IHalfEdge

, F extends IFace

> implements Iterator { private E currentHalfEdge; private E edge; private boolean started = false; - private IMesh mesh; + private IMesh mesh; - public EdgeIterator(final IMesh mesh, final F face){ + public EdgeIterator(final IMesh mesh, final F face){ this.edge = mesh.getEdge(face); this.currentHalfEdge = edge; this.mesh = mesh; diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/BasePointLocator.java b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/BasePointLocator.java index 7b3ac74d866ab8d4e11c9f013e0fdf277c96b91a..9e5194267bc62609d89eae7e42a64a0b49a05efa 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/BasePointLocator.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/BasePointLocator.java @@ -2,13 +2,11 @@ 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.IVertex; import org.vadere.util.geometry.shapes.IPoint; -import java.util.Collection; -import java.util.Collections; import java.util.Optional; /** @@ -16,47 +14,53 @@ import java.util.Optional; * * The BasePointLocatetor only uses the mesh itself and does not use any additional data structure * to find the face for a given point. It runs a march starting from some (not known) face of the - * mesh and end up at the face that contains the point. In worst case this is not faster than + * mesh and end up at the face that triangleContains the point. In worst case this is not faster than * checking each each face of the mesh but it is more clever and faste in the most cases. * * * @param

+ * @param * @param * @param */ -public class BasePointLocator

, F extends IFace

> implements IPointLocator { +public class BasePointLocator

, E extends IHalfEdge

, F extends IFace

> implements IPointLocator { - private ITriConnectivity triConnectivity; + private ITriConnectivity triConnectivity; - public BasePointLocator(final ITriConnectivity triConnectivity) { + public BasePointLocator(final ITriConnectivity triConnectivity) { this.triConnectivity = triConnectivity; } @Override - public void splitFaceEvent(final F original, final F[] faces) {} + public void splitTriangleEvent(F original, F f1, F f2, F f3) {} + + @Override + public void splitEdgeEvent(F original, F f1, F f2) {} @Override public void flipEdgeEvent(final F f1, final F f2) {} @Override - public void insertEvent(E vertex) {} + public void insertEvent(V vertex) {} @Override public void deleteBoundaryFace(final F face) {} @Override - public Collection locatePoint(final IPoint point, boolean insertion) { - if(insertion) { - return triConnectivity.getAdjacentFaces(point.getX(), point.getY()); + public F locatePoint(final P point, boolean insertion) { + //return triConnectivity.getMesh().getFace(triConnectivity.locateNearestNeighbour(point)); + return triConnectivity.locateFace(point.getX(), point.getY()).get(); + /*if(insertion) { + return triConnectivity.getClosestEdge(point.getX(), point.getY()); } else { - Optional optFace = triConnectivity.locate(point.getX(), point.getY()); + Optional optFace = triConnectivity.locateFace(point.getX(), point.getY()); return optFace.isPresent() ? Collections.singleton(optFace.get()) : Collections.emptyList(); - } + }*/ } @Override public Optional locate(final IPoint point) { - return triConnectivity.locate(point.getX(), point.getY()); + return triConnectivity.locateFace(point.getX(), point.getY()); } } diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/DelaunayHierarchy.java b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/DelaunayHierarchy.java index a858fac896deda8f6e4293012168fe690c78f9d9..9e02257c0e13346ddfdadc2ac512dceed4f7337b 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/DelaunayHierarchy.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/DelaunayHierarchy.java @@ -1,19 +1,20 @@ package org.vadere.util.geometry.mesh.triangulations; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; 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.mesh.inter.IVertex; import org.vadere.util.geometry.shapes.IPoint; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -27,137 +28,219 @@ import java.util.function.Supplier; * @param * @param */ -public class DelaunayHierarchy

, F extends IFace

> implements IPointLocator { +public class DelaunayHierarchy

, E extends IHalfEdge

, F extends IFace

> implements IPointLocator { + private static Logger log = LogManager.getLogger(DelaunayHierarchy.class); - private List> hierarchySets; + private List> hierarchySets; - private List> hierarchyConnector; + private List> hierarchyConnector; - private ITriangulation base; + private ITriangulation base; - private Supplier> triangulationSupplier; + private Supplier> triangulationSupplier; // see delaunay-hierarchy paper! - private double alpha = 40; + private double alpha = 30; + + private int maxLevel = 5; + + private int minSize = 20; + + private LinkedList prevLocationResult; + + private P prevLocationPoint; private double epsilon = 0.00001; private Random random; - public DelaunayHierarchy(final ITriangulation base, final Supplier> triangulationSupplier) { - this.hierarchySets = new ArrayList<>(); - this.hierarchyConnector = new ArrayList<>(); + public DelaunayHierarchy(final ITriangulation base, final Supplier> triangulationSupplier) { + this.hierarchySets = new ArrayList<>(maxLevel); + this.hierarchyConnector = new ArrayList<>(maxLevel); this.random = new Random(); this.triangulationSupplier = triangulationSupplier; this.base = base; - this.base.init(); + this.prevLocationPoint = null; + this.prevLocationResult = null; + this.init(); + } + private void init() { + base.init(); hierarchySets.add(base); hierarchyConnector.add(new HashMap<>()); + + for(int i = 1; i <= maxLevel; i++) { + ITriangulation triangulation = triangulationSupplier.get(); + triangulation.init(); + hierarchySets.add(triangulation); + + List superTriangleVertices = triangulation.getSuperVertices(); + List superTrianglesLastVertices = getLevel(i-1).getSuperVertices(); + + for(int j = 0; j < superTriangleVertices.size(); j++) { + //i -> i -1 + setDown(superTriangleVertices.get(j), superTrianglesLastVertices.get(j), i); + } + hierarchyConnector.add(new HashMap<>()); + } } @Override - public void splitFaceEvent(F original, F[] faces) {} + public void splitTriangleEvent(F original, F f1, F f2, F f3) {} + + @Override + public void splitEdgeEvent(F original, F f1, F f2) {} @Override public void flipEdgeEvent(F f1, F f2) {} @Override - public void insertEvent(@NotNull final E halfEdge) { - P vertex = base.getMesh().getVertex(halfEdge); - E lastEdge = halfEdge; - int limit = hierarchySets.size(); - for(int i = 1; i <= limit; ++i) { - - if(random.nextDouble() < alpha) { - - if(hierarchySets.size() <= i) { - ITriangulation triangulation = triangulationSupplier.get(); - triangulation.init(); - hierarchySets.add(triangulation); - } + public void insertEvent(@NotNull final V vertex) { + P p = base.getMesh().getPoint(vertex); + V prev = vertex; - E edge = hierarchySets.get(i).insert(vertex); + if(!p.equals(prevLocationPoint)) { + prevLocationResult = locateAll(p); + prevLocationPoint = p; + } - if(hierarchyConnector.size() < i) { - hierarchyConnector.add(new HashMap<>()); - } + int vertexLevel = randomLevel(); - hierarchyConnector.get(i-1).put(edge, lastEdge); - lastEdge = edge; + if(vertexLevel >= 1) { + Iterator locatedFaces = prevLocationResult.iterator(); + // skip the 0 level, since the point is already inserted + locatedFaces.next(); + for(int i = 1; i <= vertexLevel; i++) { + V v; + if(locatedFaces.hasNext()) { + v = getLevel(i).getMesh().getVertex(getLevel(i).insert(p, locatedFaces.next())); + } + else { + v = getLevel(i).getMesh().getVertex(getLevel(i).insert(p)); + } + + setDown(v, prev, i); + prev = v; } - else { - break; - } } } + private ITriangulation getLevel(final int level) { + if(level > maxLevel) { + throw new IllegalArgumentException("level is greater than the maximum level."); + } + + return hierarchySets.get(level); + } + + private void setDown(V src, V dest, int srcLevel) { + getLevel(srcLevel).getMesh().setDown(src, dest); + } + + private V getDown(V src, int srcLevel) { + return getLevel(srcLevel).getMesh().getDown(src); + } + + private int randomLevel() { + int level = 0; + while (random.nextDouble() < 1/alpha && level < maxLevel) { + level++; + } + return level; + } + @Override public void deleteBoundaryFace(F face) { } @Override - public Collection locatePoint(P point, boolean insertion) { + public F locatePoint(P point, boolean insertion) { Optional optFace = locate(point); - if(optFace.isPresent()) { - F face = optFace.get(); + return optFace.get(); + } + else { + throw new IllegalArgumentException(point + " is invalid, it can not be located by " + this); + } + } - if(!insertion) { - return Collections.singleton(face); - } - else { - Optional optEdge = base.getMesh().getMemberEdge(face, point.getX(), point.getY(), epsilon); + private LinkedList locateAll(final P point) { + // find the highest level with enough vertices + int level = maxLevel; + + while (level > 0 && getLevel(level).getMesh().getNumberOfVertices() < minSize) { + level--; + } - // ignore point - if(optEdge.isPresent()) { - return Collections.emptyList(); + LinkedList faces = new LinkedList<>(); + V v = null; + F face = null; + while (level >= 0) { + ITriangulation tri = getLevel(level); + + //TODO: SE-Architecture dirty here! + if(v == null) { + if(level == 0) { + face = tri.marchRandom2D(point.getX(), point.getY(), tri.getMesh().getFace()); } else { - optEdge = base.getMesh().getEdgeCloseToVertex(face, point.getX(), point.getY(), epsilon); - if(optEdge.isPresent()) { - return Arrays.asList(base.getMesh().getFace(optEdge.get()), base.getMesh().getTwinFace(optEdge.get())); - } - else { - return Collections.singleton(face); - } + face = tri.locateFace(point).get(); } - } - } - else { - return Collections.emptyList(); - } - } - @Override - public Optional locate(final P point) { - Optional optStartFace = Optional.empty(); - for(int i = hierarchySets.size()-1; i >= 0; --i) { - if(i == hierarchySets.size()-1) { - optStartFace = hierarchySets.get(i).locate(point.getX(), point.getY()); + v = tri.getMesh().closestVertex(face, point.getX(), point.getY()); + //tri.getMesh().getVertex(tri.getMesh().getEdge(face)); + //v = tri.locateNearestNeighbour(point, face); } else { - if(i > 0) { - E edge = getNearestPoint(hierarchySets.get(i+1), optStartFace.get(), point); - E newEdge = hierarchyConnector.get(i).get(edge); - optStartFace = hierarchySets.get(i).locate(point.getX(), point.getY(), hierarchySets.get(i).getMesh().getFace(newEdge)); + // level+1 -> level + v = getDown(v, level+1); + + //TODO: SE-Architecture dirty here! + if(level == 0) { + face = tri.marchRandom2D(point.getX(), point.getY(), tri.getMesh().getFace(v)); } else { - E edge = getNearestPoint(hierarchySets.get(i+1), optStartFace.get(), point); - E newEdge = hierarchyConnector.get(i).get(edge); - return base.locate(point.getX(), point.getY(), base.getMesh().getFace(newEdge)); + face = tri.locateFace(point, tri.getMesh().getFace(v)).get(); } + //v = tri.locateNearestNeighbour(point, face); + v = tri.getMesh().closestVertex(face, point.getX(), point.getY()); } + + faces.addFirst(face); + level--; } - return optStartFace; + return faces; + } + + @Override + public Optional locate(final P point) { + LinkedList allFaces = locateAll(point); + prevLocationResult = allFaces; + prevLocationPoint = point; + if(!allFaces.isEmpty()) { + return Optional.of(allFaces.getFirst()); + } + else { + return Optional.empty(); + } } - public E getNearestPoint(final ITriangulation triangulation, final F face, final P point) { - IMesh mesh = triangulation.getMesh(); - return triangulation.getMesh().streamEdges(face).reduce((p1, p2) -> mesh.getVertex(p1).distance(point) > mesh.getVertex(p2).distance(point) ? p2 : p1).get(); + /** + * Returns vertex of the triangulation of the face with the smallest distance to point. + * Assumption: The face has to be part of the mesh of the triangulation. + * + * @param triangulation the triangulation + * @param face the face of the trianuglation + * @param point the point + * @return vertex of the triangulation of the face with the smallest distance to point + */ + public V getNearestPoint(final ITriangulation triangulation, final F face, final P point) { + IMesh mesh = triangulation.getMesh(); + return mesh.streamEdges(face).map(edge -> mesh.getVertex(edge)).reduce((p1, p2) -> p1.distance(point) > p2.distance(point) ? p2 : p1).get(); } } diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/DelaunayTree.java b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/DelaunayTree.java index 146b9cd14a24bb31e5aef8333228cfdb84910e1e..e39b8a3b0bb7a79a11b98d127bdf933ff8dcb625 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/DelaunayTree.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/DelaunayTree.java @@ -7,6 +7,7 @@ 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.ITriangulation; +import org.vadere.util.geometry.mesh.inter.IVertex; import org.vadere.util.geometry.shapes.IPoint; import java.util.Collection; @@ -18,13 +19,13 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -public class DelaunayTree

, F extends IFace

> implements IPointLocator { +public class DelaunayTree

, E extends IHalfEdge

, F extends IFace

> implements IPointLocator { private DAG> dag; private final HashMap>> map; - private final IMesh mesh; + private final IMesh mesh; private double eps = 0.0000001; - public DelaunayTree(final ITriangulation triangulation) { + public DelaunayTree(final ITriangulation triangulation) { this.mesh = triangulation.getMesh(); this.map = new HashMap<>(); } @@ -42,9 +43,9 @@ public class DelaunayTree

, F extends IF } @Override - public Collection locatePoint(final P point, final boolean insertion) { + public F locatePoint(final P point, final boolean insertion) { checkRoot(); - + Set>> leafs = new HashSet<>(); LinkedList>> nodesToVisit = new LinkedList<>(); nodesToVisit.add(dag); @@ -56,9 +57,7 @@ public class DelaunayTree

, F extends IF leafs.add(currentNode); // if we are not interested in insertion we just want to find one triangle. - if(!insertion) { - return leafs.stream().map(dag -> dag.getElement().getFace()).collect(Collectors.toList()); - } + return currentNode.getElement().getFace(); } else { nodesToVisit.addAll(currentNode.getChildren()); @@ -66,31 +65,55 @@ public class DelaunayTree

, F extends IF } } - return leafs.stream().map(dag -> dag.getElement().getFace()).collect(Collectors.toList()); + throw new IllegalArgumentException(point + " is invalid, it can not be located by " + this); } @Override public Optional locate(final P point) { checkRoot(); - Optional optFace = locatePoint(point, false).stream().findAny(); - if(optFace.isPresent()) { - return Optional.of(optFace.get()); - } - else { - return Optional.empty(); - } + return Optional.of(locatePoint(point, false)); } @Override - public void splitFaceEvent(F original, F[] faces) { + public void splitTriangleEvent(F original, F f1, F f2, F f3) { checkRoot(); DAG> faceDag = map.remove(original); - for(F face : faces) { - List

points = mesh.getVertices(face); - DAG> newFaceDag = new DAG<>(new DAGElement(face, Triple.of(points.get(0), points.get(1), points.get(2)))); - faceDag.addChild(newFaceDag); - map.put(face, newFaceDag); - } + + F face = f1; + List points1 = mesh.getVertices(face); + DAG> newFaceDag1 = new DAG<>(new DAGElement(face, Triple.of(points1.get(0), points1.get(1), points1.get(2)))); + faceDag.addChild(newFaceDag1); + map.put(face, newFaceDag1); + + face = f2; + List points2 = mesh.getVertices(face); + DAG> newFaceDag2 = new DAG<>(new DAGElement(face, Triple.of(points2.get(0), points2.get(1), points2.get(2)))); + faceDag.addChild(newFaceDag2); + map.put(face, newFaceDag2); + + face = f3; + List points3 = mesh.getVertices(face); + DAG> newFaceDag3 = new DAG<>(new DAGElement(face, Triple.of(points3.get(0), points3.get(1), points3.get(2)))); + faceDag.addChild(newFaceDag3); + map.put(face, newFaceDag3); + } + + @Override + public void splitEdgeEvent(F original, F f1, F f2) { + checkRoot(); + DAG> faceDag = map.remove(original); + + F face = f1; + List points1 = mesh.getVertices(face); + DAG> newFaceDag1 = new DAG<>(new DAGElement(face, Triple.of(points1.get(0), points1.get(1), points1.get(2)))); + faceDag.addChild(newFaceDag1); + map.put(face, newFaceDag1); + + face = f2; + List points2 = mesh.getVertices(face); + DAG> newFaceDag2 = new DAG<>(new DAGElement(face, Triple.of(points2.get(0), points2.get(1), points2.get(2)))); + faceDag.addChild(newFaceDag2); + map.put(face, newFaceDag2); } @Override @@ -98,8 +121,8 @@ public class DelaunayTree

, F extends IF checkRoot(); DAG> f1Dag = map.remove(f1); DAG> f2Dag = map.remove(f2); - List

points1 = mesh.getVertices(f1); - List

points2 = mesh.getVertices(f2); + List points1 = mesh.getVertices(f1); + List points2 = mesh.getVertices(f2); DAG> newf1Dag = new DAG<>(new DAGElement(f1, Triple.of(points1.get(0), points1.get(1), points1.get(2)))); DAG> newf2Dag = new DAG<>(new DAGElement(f2, Triple.of(points2.get(0), points2.get(1), points2.get(2)))); @@ -115,7 +138,7 @@ public class DelaunayTree

, F extends IF } @Override - public void insertEvent(E vertex) {} + public void insertEvent(V vertex) {} @Override public void deleteBoundaryFace(F face) { diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/IncrementalTriangulation.java b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/IncrementalTriangulation.java index bf1fd5c652f5af8735d1096c3782009f8e179c25..7f46bd3af19ab259d836528718f8bd39f6258f94 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/IncrementalTriangulation.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/IncrementalTriangulation.java @@ -6,6 +6,8 @@ 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.impl.PVertex; +import org.vadere.util.geometry.mesh.inter.IVertex; import org.vadere.util.geometry.mesh.iterators.FaceIterator; import org.vadere.util.geometry.mesh.inter.IFace; import org.vadere.util.geometry.mesh.inter.IHalfEdge; @@ -19,7 +21,6 @@ import org.vadere.util.geometry.shapes.VLine; import org.vadere.util.geometry.shapes.VPoint; import org.vadere.util.geometry.shapes.VRectangle; import org.vadere.util.geometry.shapes.VTriangle; -import org.vadere.util.triangulation.BowyerWatsonSlow; import org.vadere.util.triangulation.IPointConstructor; import org.vadere.util.geometry.mesh.inter.IPointLocator; import org.vadere.util.geometry.mesh.inter.ITriangulation; @@ -41,25 +42,28 @@ import java.util.stream.StreamSupport; import javax.swing.*; -public class IncrementalTriangulation

, F extends IFace

> implements ITriangulation { +public class IncrementalTriangulation

, E extends IHalfEdge

, F extends IFace

> implements ITriangulation { - protected Set

points; + protected Collection

points; // TODO: use symbolic for the super-triangle points instead of real points! - private P p0; - private P p1; - private P p2; + private V p0; + private V p1; + private V p2; private E he0; private E he1; private E he2; private final VRectangle bound; private boolean finalized = false; - private IMesh mesh; - private IPointLocator pointLocator; + private IMesh mesh; + private IPointLocator pointLocator; private boolean initialized = false; + private List superEdges; + private int maxDepth = 0; // TODO this epsilon it hard coded!!! => replace it with a user choice private double epsilon = 0.0001; + private double edgeCoincidenceTolerance = 0.0001; protected F superTriangle; private F borderFace; @@ -67,7 +71,7 @@ public class IncrementalTriangulation

, private static Logger log = LogManager.getLogger(IncrementalTriangulation.class); public IncrementalTriangulation( - final Set

points, + final Collection

points, final Predicate illegalPredicate) { this.points = points; this.illegalPredicate = illegalPredicate; @@ -92,11 +96,11 @@ public class IncrementalTriangulation

, this(bound, halfEdge -> true); } - public void setPointLocator(final IPointLocator pointLocator) { + public void setPointLocator(final IPointLocator pointLocator) { this.pointLocator = pointLocator; } - public void setMesh(final IMesh mesh) { + public void setMesh(final IMesh mesh) { this.mesh = mesh; } @@ -116,9 +120,15 @@ public class IncrementalTriangulation

, he1 = borderEdges.get(1); he2 = borderEdges.get(2); + this.superEdges = Arrays.asList(p0, p1, p2); this.initialized = true; } + @Override + public List getSuperVertices() { + return superEdges; + } + @Override public void compute() { @@ -134,50 +144,47 @@ public class IncrementalTriangulation

, } @Override - public E insert(P point) { - + public E insert(@NotNull P point, @NotNull F face) { if(!initialized) { init(); } - Collection faces = this.pointLocator.locatePoint(point, true); - int numberOfFaces = faces.size(); - - // problem due numerical calculation. - faces = faces.stream().filter(f -> !getMesh().isMember(f, point.getX(), point.getY(), epsilon)).collect(Collectors.toList()); - E insertedEdge = null; - - if(numberOfFaces == 0) { - log.warn("no face found at " + point); - } - else if(faces.size() == 1) { - log.info("splitTriangle:" + point); - F face = faces.iterator().next(); - List createdFaces = splitTriangle(face, point, true); - insertedEdge = mesh.getMemberEdge(createdFaces.get(0), point.getX(), point.getY()).get(); - insertEvent(insertedEdge); - - } // point lies on an edge of 2 triangles - else if(faces.size() == 2) { - Iterator it = faces.iterator(); - log.info("splitEdge:" + point); - E halfEdge = findTwins(it.next(), it.next()).get(); - List newEdges = splitEdge(point, halfEdge, true); - insertedEdge = newEdges.stream().filter(he -> getMesh().getVertex(he).equals(point)).findAny().get(); - insertEvent(insertedEdge); + E edge = mesh.closestEdge(face, point.getX(), point.getY()); + P p1 = mesh.getPoint(mesh.getPrev(edge)); + P p2 = mesh.getPoint(edge); + + /* + * 3 Cases: + * 1) point lies on an vertex of a face => ignore the point + * 2) point lies on an edge of a face => split the edge + * 3) point lies in the interior of the face => split the face (this should be the main case) + */ + if(isMember(face, point.getX(), point.getY(), edgeCoincidenceTolerance)) { + log.info("ignore insertion point, since the point " + point + " already exists or it is too close to another point!"); + return edge; } - else if(faces.size() == 0) { - log.warn("ignore insertion point, since the point " + point + " already exists or it is too close to another point!"); + if(GeometryUtils.isOnEdge(p1, p2, point, edgeCoincidenceTolerance)) { + //log.info("splitEdge()"); + E newEdge = splitEdge(point, edge, true); + insertEvent(newEdge); + return newEdge; } else { - log.error("more than 2 faces found and the point " + point + " is not a vertex jet, there is something wrong with the mesh structure!"); + //log.info("splitTriangle()"); + E newEdge = splitTriangle(face, point, true); + insertEvent(newEdge); + return newEdge; } + } - return insertedEdge; + @Override + public E insert(P point) { + F face = this.pointLocator.locatePoint(point, true); + return insert(point, face); } @Override - public void insert(final Set

points) { + public void insert(final Collection

points) { if(!initialized) { init(); } @@ -226,13 +233,13 @@ public class IncrementalTriangulation

, } /** - * Deletes a face assuming that the face contains at least one boundary edge, otherwise the + * Deletes a face assuming that the face triangleContains at least one boundary edge, otherwise the * deletion will not result in an feasibly triangulation. * * @param face the face that will be deleted, which as to be adjacent to the boundary. */ public void deleteBoundaryFace(final F face) { - assert isDeletionOk(face); + //assert isDeletionOk(face); // 3 cases: 1. triangle consist of 1, 2 or 3 boundary edges List boundaryEdges = new ArrayList<>(3); @@ -291,12 +298,12 @@ public class IncrementalTriangulation

, } @Override - public IMesh getMesh() { + public IMesh getMesh() { return mesh; } @Override - public Optional locate(final P point) { + public Optional locateFace(final P point) { return pointLocator.locate(point); } @@ -329,10 +336,10 @@ public class IncrementalTriangulation

, } private VTriangle faceToTriangle(final F face) { - List

points = mesh.getEdges(face).stream().map(edge -> mesh.getVertex(edge)).collect(Collectors.toList()); - P p1 = points.get(0); - P p2 = points.get(1); - P p3 = points.get(2); + List points = mesh.getEdges(face).stream().map(edge -> mesh.getVertex(edge)).collect(Collectors.toList()); + V p1 = points.get(0); + V p2 = points.get(1); + V p3 = points.get(2); return new VTriangle(new VPoint(p1.getX(), p1.getY()), new VPoint(p2.getX(), p2.getY()), new VPoint(p3.getX(), p3.getY())); } @@ -346,23 +353,76 @@ public class IncrementalTriangulation

, * @return true if the edge with respect to p is illegal, otherwise false */ @Override - public boolean isIllegal(E edge) { - return isIllegal(edge, mesh); + public boolean isIllegal(E edge, V p) { + if(!mesh.isBoundary(mesh.getTwinFace(edge))) { + //assert mesh.getVertex(mesh.getNext(edge)).equals(p); + //V p = mesh.getVertex(mesh.getNext(edge)); + E t0 = mesh.getTwin(edge); + E t1 = mesh.getNext(t0); + E t2 = mesh.getNext(t1); + + V x = mesh.getVertex(t0); + V y = mesh.getVertex(t1); + V z = mesh.getVertex(t2); + + //return GeometryUtils.angle(x, y, z) + GeometryUtils.angle(x, p, z) > Math.PI; + + //return GeometryUtils.isInCircumscribedCycle(x, y, z, p); + //if(GeometryUtils.ccw(z,x,y) > 0) { + return GeometryUtils.isInsideCircle(z, x, y, p); + //} + //else { + // return GeometryUtils.isInsideCircle(x, z, y, p); + //} + } + + return false; + //return isIllegal(edge, p, mesh); } - public static

, F extends IFace

> boolean isIllegal(E edge, IMesh mesh) { + /*public static

, E extends IHalfEdge

, F extends IFace

> boolean isIllegal(E edge, V p, IMesh mesh) { if(!mesh.isBoundary(mesh.getTwinFace(edge))) { - P p = mesh.getVertex(mesh.getNext(edge)); + //V p = mesh.getVertex(mesh.getNext(edge)); E t0 = mesh.getTwin(edge); E t1 = mesh.getNext(t0); E t2 = mesh.getNext(t1); - P x = mesh.getVertex(t0); - P y = mesh.getVertex(t1); - P z = mesh.getVertex(t2); + V x = mesh.getVertex(t0); + V y = mesh.getVertex(t1); + V z = mesh.getVertex(t2); - VTriangle triangle = new VTriangle(new VPoint(x), new VPoint(y), new VPoint(z)); - return triangle.isInCircumscribedCycle(p); + //return GeometryUtils.angle(x, y, z) + GeometryUtils.angle(x, p, z) > Math.PI; + + //return GeometryUtils.isInCircumscribedCycle(x, y, z, p); + if (GeometryUtils.ccw(x,y,z) > 0 + t.dest().rightOf(e) && v.isInCircle(e.orig(), t.dest(), e.dest())) { + log.info(GeometryUtils.ccw(x,y,z) > 0); + return GeometryUtils.isInsideCircle(x, y, z, p); + } + return false; + }*/ + + public static

, E extends IHalfEdge

, F extends IFace

> boolean isIllegal(E edge, V p, IMesh mesh) { + if(!mesh.isBoundary(mesh.getTwinFace(edge))) { + //assert mesh.getVertex(mesh.getNext(edge)).equals(p); + //V p = mesh.getVertex(mesh.getNext(edge)); + E t0 = mesh.getTwin(edge); + E t1 = mesh.getNext(t0); + E t2 = mesh.getNext(t1); + + V x = mesh.getVertex(t0); + V y = mesh.getVertex(t1); + V z = mesh.getVertex(t2); + + //return GeometryUtils.angle(x, y, z) + GeometryUtils.angle(x, p, z) > Math.PI; + + //return GeometryUtils.isInCircumscribedCycle(x, y, z, p); + //if(GeometryUtils.ccw(z,x,y) > 0) { + return GeometryUtils.isInsideCircle(z, x, y, p); + //} + //else { + // return GeometryUtils.isInsideCircle(x, z, y, p); + //} } return false; } @@ -380,19 +440,58 @@ public class IncrementalTriangulation

, return false; }*/ + @Override + public void legalizeNonRecursive(@NotNull final E edge, final V p) { + int flips = 0; + int its = 0; + //if(isIllegal(edge, p)) { + + // this should be the same afterwards + //E halfEdge = getMesh().getNext(edge); + + IMesh mesh = getMesh(); + E startEdge = mesh.getPrev(edge); + E endEdge = mesh.getTwin(getMesh().getPrev(startEdge)); + E currentEdge = mesh.getPrev(edge); + + // flipp + //c.prev.twin + + while(currentEdge != endEdge) { + while (isIllegal(mesh.getNext(currentEdge), p)) { + flip(mesh.getNext(currentEdge)); + flips++; + its++; + } + + its++; + + currentEdge = mesh.getTwin(mesh.getPrev(currentEdge)); + } + + //log.info("#flips = " + flips); + //log.info("#its = " + its); + //} + } + @Override public void flipEdgeEvent(final F f1, final F f2) { pointLocator.flipEdgeEvent(f1, f2); } @Override - public void splitFaceEvent(final F original, final F[] faces) { - pointLocator.splitFaceEvent(original, faces); + public void splitTriangleEvent(final F original, final F f1, F f2, F f3) { + pointLocator.splitTriangleEvent(original, f1, f2, f3); + } + + @Override + public void splitEdgeEvent(F original, F f1, F f2) { + pointLocator.splitEdgeEvent(original, f1, f2); } @Override - public void insertEvent(@NotNull final E halfEdge) { - pointLocator.insertEvent(halfEdge); + public void insertEvent(@NotNull final E halfEdge) { + pointLocator.insertEvent(getMesh().getVertex(halfEdge)); } @Override @@ -418,7 +517,7 @@ public class IncrementalTriangulation

, points.add(new VPoint(80,70));*/ Random r = new Random(); - for(int i=0; i< 1000; i++) { + for(int i=0; i< 1000000; i++) { VPoint point = new VPoint(width*r.nextDouble(), height*r.nextDouble()); points.add(point); } @@ -427,13 +526,14 @@ public class IncrementalTriangulation

, long ms = System.currentTimeMillis(); PMesh mesh = new PMesh<>(pointConstructor); - IncrementalTriangulation, PFace> bw = new IncrementalTriangulation<>(points); - bw.setMesh(mesh); - bw.setPointLocator(new DelaunayTree<>(bw)); - bw.compute(); + ITriangulation, PHalfEdge, PFace> bw = ITriangulation.createPTriangulation( + IPointLocator.Type.DELAUNAY_HIERARCHY, + points, + pointConstructor); + new IncrementalTriangulation<>(points); + bw.finalize(); Set 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); JFrame window = new JFrame(); @@ -442,7 +542,7 @@ public class IncrementalTriangulation

, window.getContentPane().add(new Lines(edges, points, max)); window.setVisible(true); - ms = System.currentTimeMillis(); + /*ms = System.currentTimeMillis(); BowyerWatsonSlow bw2 = new BowyerWatsonSlow(points, (x, y) -> new VPoint(x, y)); bw2.execute(); Set edges2 = bw2.getTriangles().stream() @@ -455,7 +555,7 @@ public class IncrementalTriangulation

, window2.getContentPane().add(new Lines(edges2, points, max)); window2.setVisible(true); - UniformTriangulation, PFace> uniformTriangulation = ITriangulation.createUnifirmTriangulation( + UniformTriangulation, PHalfEdge, PFace> uniformTriangulation = ITriangulation.createUnifirmTriangulation( IPointLocator.Type.DELAUNAY_TREE, new VRectangle(0, 0, width, height), 10.0, @@ -483,7 +583,7 @@ public class IncrementalTriangulation

, window4.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); window4.setBounds(0, 0, max, max); window4.getContentPane().add(new Lines(edges4, edges4.stream().flatMap(edge -> edge.streamPoints()).collect(Collectors.toSet()), max)); - window4.setVisible(true); + window4.setVisible(true);*/ } private static class Lines extends JComponent{ diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/UniformRefinementTriangulation.java b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/UniformRefinementTriangulation.java index 1da05aeada76b7ee2ffba0cb5a43332d8d9a81ca..8b5aab28e9ab893b33322c7af1c0a444307fc565 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/UniformRefinementTriangulation.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/UniformRefinementTriangulation.java @@ -4,6 +4,7 @@ import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.vadere.util.geometry.mesh.impl.PFace; import org.vadere.util.geometry.mesh.impl.PHalfEdge; +import org.vadere.util.geometry.mesh.impl.PVertex; import org.vadere.util.geometry.mesh.inter.IMesh; import org.vadere.util.geometry.mesh.inter.IPointLocator; import org.vadere.util.geometry.mesh.inter.ITriangulation; @@ -32,9 +33,9 @@ public class UniformRefinementTriangulation

{ private final Collection boundary; private final VRectangle bbox; private final IEdgeLengthFunction lenFunc; - private ITriangulation, PFace

> pTriangulation; + private ITriangulation, PHalfEdge

, PFace

> pTriangulation; private Set

points; - private IMesh, PFace

> mesh; + private IMesh, PHalfEdge

, PFace

> mesh; private static final Logger logger = LogManager.getLogger(UniformRefinementTriangulation.class); public UniformRefinementTriangulation( @@ -53,8 +54,8 @@ public class UniformRefinementTriangulation

{ public boolean isCompleted(final PHalfEdge

edge) { PFace

face = mesh.isBoundary(edge) ? mesh.getTwinFace(edge) : mesh.getFace(edge); VTriangle triangle = mesh.toTriangle(face); - P end = mesh.getVertex(edge); - P begin = mesh.getVertex(mesh.getPrev(edge)); + P end = mesh.getPoint(edge); + P begin = mesh.getPoint(mesh.getPrev(edge)); return !bbox.intersect(triangle) || boundary.stream().anyMatch(shape -> shape.contains(triangle.getBounds2D())) || begin.distance(end) <= lenFunc.apply(midPoint(edge)); } @@ -89,7 +90,7 @@ public class UniformRefinementTriangulation

{ // 1. find a triangle inside the boundary VPoint centroid = shape.getCentroid(); - Optional> optFace = pTriangulation.locate(centroid.getX(), centroid.getY()); + Optional> optFace = pTriangulation.locateFace(centroid.getX(), centroid.getY()); if(optFace.isPresent()) { LinkedList> candidates = new LinkedList<>(); @@ -116,15 +117,15 @@ public class UniformRefinementTriangulation

{ public Collection> refine(final PHalfEdge

edge) { VPoint midPoint = midPoint(edge); - P p = mesh.createVertex(midPoint.getX(), midPoint.getY()); + P p = mesh.createPoint(midPoint.getX(), midPoint.getY()); if(points.contains(p)) { return Collections.emptyList(); } else { points.add(p); - mesh.insert(p); - return pTriangulation.splitEdge(p, edge); + PHalfEdge

createdEdge = pTriangulation.splitEdge(p, edge); + return mesh.getIncidentEdges(createdEdge); } } diff --git a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/UniformTriangulation.java b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/UniformTriangulation.java index f26bac05f533b67abd838a790170972504d5fd3f..55bf5709391118606035fab3e5865697166b6b2f 100644 --- a/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/UniformTriangulation.java +++ b/VadereUtils/src/org/vadere/util/geometry/mesh/triangulations/UniformTriangulation.java @@ -3,6 +3,7 @@ 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.IVertex; import org.vadere.util.geometry.shapes.IPoint; import org.vadere.util.geometry.shapes.VRectangle; import org.vadere.util.triangulation.IPointConstructor; @@ -15,7 +16,7 @@ import java.util.List; import java.util.Set; -public class UniformTriangulation

, F extends IFace

> extends IncrementalTriangulation { +public class UniformTriangulation

, E extends IHalfEdge

, F extends IFace

> extends IncrementalTriangulation { private double left; private double top; @@ -54,22 +55,22 @@ public class UniformTriangulation

, F ex double h = minTriangleSideLength * Math.sqrt(3) / 2.0; // create stencil with four triangle which can be used to triangulate // the whole rectangle seamlessly - P add1 = getMesh().createVertex(-s / 2, h); - P add2 = getMesh().createVertex(s / 2, h); - P add3 = getMesh().createVertex(s, 0); - P add4 = getMesh().createVertex(0, 2 * h); - P add5 = getMesh().createVertex(s, 2 * h); + P add1 = getMesh().createPoint(-s / 2, h); + P add2 = getMesh().createPoint(s / 2, h); + P add3 = getMesh().createPoint(s, 0); + P add4 = getMesh().createPoint(0, 2 * h); + P add5 = getMesh().createPoint(s, 2 * h); for (int row = 0; row < (int) Math.ceil(height / h) + 1; row += 2) { for (int col = 0; col < (int) Math.ceil(width / minTriangleSideLength); col++) { - P p1 = getMesh().createVertex(left + col * minTriangleSideLength, top + row * h); + P p1 = getMesh().createPoint(left + col * minTriangleSideLength, top + row * h); - P p2 = getMesh().createVertex(p1.getX() + add1.getX(), p1.getY() + add1.getY()); - P p3 = getMesh().createVertex(p1.getX() + add2.getX(), p1.getY() + add2.getY()); - P p4 = getMesh().createVertex(p1.getX() + add3.getX(), p1.getY() + add3.getY()); - P p5 = getMesh().createVertex(p1.getX() + add4.getX(), p1.getY() + add4.getY()); - P p6 = getMesh().createVertex(p1.getX() + add5.getX(), p1.getY() + add5.getY()); + P p2 = getMesh().createPoint(p1.getX() + add1.getX(), p1.getY() + add1.getY()); + P p3 = getMesh().createPoint(p1.getX() + add2.getX(), p1.getY() + add2.getY()); + P p4 = getMesh().createPoint(p1.getX() + add3.getX(), p1.getY() + add3.getY()); + P p5 = getMesh().createPoint(p1.getX() + add4.getX(), p1.getY() + add4.getY()); + P p6 = getMesh().createPoint(p1.getX() + add5.getX(), p1.getY() + add5.getY()); pointSet.add(p1); pointSet.add(p2); diff --git a/VadereUtils/src/org/vadere/util/triangulation/PointLocation.java b/VadereUtils/src/org/vadere/util/triangulation/PointLocation.java index 766a616394be7de44b738b761b95d0051f99227c..7cddd79ae346539cdbe8a2adb99a7690ae46d7c0 100644 --- a/VadereUtils/src/org/vadere/util/triangulation/PointLocation.java +++ b/VadereUtils/src/org/vadere/util/triangulation/PointLocation.java @@ -2,12 +2,14 @@ package org.vadere.util.triangulation; import org.vadere.util.geometry.mesh.impl.PFace; import org.vadere.util.geometry.mesh.impl.PHalfEdge; +import org.vadere.util.geometry.mesh.impl.PVertex; import org.vadere.util.geometry.mesh.inter.IMesh; import org.vadere.util.geometry.shapes.IPoint; import org.vadere.util.geometry.shapes.VLine; import org.vadere.util.geometry.shapes.VPoint; import org.vadere.util.geometry.shapes.VPolygon; +import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -25,7 +27,7 @@ public class PointLocation

{ private final List

orderedPointList; private final List>> halfeEdgesSegments; private final List> intersectionPointsInSegment; - private final IMesh, PFace

> mesh; + private final IMesh, PHalfEdge

, PFace

> mesh; private Comparator

pointComparatorX = (p1, p2) -> { double dx = p1.getX() - p2.getX(); @@ -53,9 +55,9 @@ public class PointLocation

{ @Override public boolean test(final PHalfEdge

halfEdge) { - P v1 = mesh.getVertex(halfEdge); - P v2 = mesh.getVertex(mesh.getPrev(halfEdge)); - return (v1.getX() > p1.getX() && v2.getX() < p2.getX()) || (v1.getX() > p2.getX() && v2.getX() < p1.getX()); + P v1 = mesh.getPoint(halfEdge); + P v2 = mesh.getPoint(mesh.getPrev(halfEdge)); + return (v1.getX() <= p1.getX() && v2.getX() >= p2.getX()) || (v2.getX() <= p1.getX() && v1.getX() >= p2.getX()); } } @@ -85,13 +87,13 @@ public class PointLocation

{ } } - public PointLocation(final Collection> faces, final IMesh, PFace

> mesh) { + public PointLocation(final Collection> faces, final IMesh, PHalfEdge

, PFace

> mesh) { this.faces = faces; this.mesh = mesh; //TODO distinct is maybe slow here Set

pointSet = faces.stream() - .flatMap(face -> mesh.streamEdges(face)).map(edge -> mesh.getVertex(edge)) + .flatMap(face -> mesh.streamEdges(face)).map(edge -> mesh.getPoint(edge)) .sorted(pointComparatorX).collect(Collectors.toSet()); orderedPointList = pointSet.stream().sorted(pointComparatorX).collect(Collectors.toList()); @@ -101,29 +103,50 @@ public class PointLocation

{ for(int i = 0; i < orderedPointList.size() - 1; i++) { P p1 = orderedPointList.get(i); P p2 = orderedPointList.get(i+1); - List> halfEdges = faces.stream().flatMap(face -> mesh.streamEdges(face)).filter(new BetweenTwoPoints(p1, p2)) + List> halfEdges = faces.stream() + .flatMap(face -> mesh.streamEdges(face)).filter(new BetweenTwoPoints(p1, p2)) .sorted(new HalfEdgeComparator(p1.getX(), p2.getX())).collect(Collectors.toList()); + List

intersectionPoints = halfEdges.stream() .map(hf -> hf.toLine()) - .map(line -> intersectionWithX(p1.getX(), line)).collect(Collectors.toList()); + .map(line -> intersectionWithX(p1.getX(), line)) + .distinct() + .collect(Collectors.toList()); + halfeEdgesSegments.add(halfEdges); + intersectionPointsInSegment.add(intersectionPoints); + + if(i == orderedPointList.size() - 2) { + intersectionPoints = Collections.singletonList(p2); + halfeEdgesSegments.add(halfEdges); + intersectionPointsInSegment.add(intersectionPoints); + } } + } private P intersectionWithX(double x, VLine line) { - return mesh.insertVertex(x, (line.getY1() + (line.getX1()-x) * line.slope())); + Point2D p = line.getX1() < line.getX2() ? line.getP1() : line.getP2(); + + return mesh.getPoint(mesh.createVertex(x, (p.getY() + (p.getX()-x) * line.slope()))); } public Optional> getFace(final P point) { - int index = Collections.binarySearch(orderedPointList, point, pointComparatorX); - int xSegmentIndex = (index >= 0) ? index : -index - 2; + int xSegmentIndex = Collections.binarySearch(orderedPointList, point, pointComparatorX); + + xSegmentIndex = xSegmentIndex < 0 ? -xSegmentIndex - 2 : xSegmentIndex; + if(xSegmentIndex < 0 || xSegmentIndex >= intersectionPointsInSegment.size()) { return Optional.empty(); } - index = Collections.binarySearch(intersectionPointsInSegment.get(xSegmentIndex), point, pointComparatorY); - int ySegmentIndex = (index >= 0) ? index : -index - 2; + List

intersectionPoints = intersectionPointsInSegment.get(xSegmentIndex); + int ySegmentIndex = Collections.binarySearch(intersectionPoints, point, pointComparatorY); + + ySegmentIndex = ySegmentIndex < 0 ? -ySegmentIndex - 2 : ySegmentIndex; + + if(ySegmentIndex < 0 || ySegmentIndex >= halfeEdgesSegments.get(xSegmentIndex).size()) { return Optional.empty(); } @@ -136,7 +159,13 @@ public class PointLocation

{ return Optional.of(face); } else { - return Optional.of(mesh.getTwinFace(edge)); + PFace

f = mesh.getTwinFace(edge); + if(mesh.isBoundary(f)) { + return Optional.empty(); + } + else { + return Optional.of(f); + } } } } diff --git a/VadereUtils/tests/org/vadere/util/geometry/TestFace.java b/VadereUtils/tests/org/vadere/util/geometry/TestFace.java index fb28ec4fa7e95938b308bfe614b4989b69dd349a..3c05b63d24474cf03fc76374d34fe743654e07ed 100644 --- a/VadereUtils/tests/org/vadere/util/geometry/TestFace.java +++ b/VadereUtils/tests/org/vadere/util/geometry/TestFace.java @@ -5,8 +5,10 @@ import org.junit.Test; import org.vadere.util.geometry.mesh.impl.PFace; import org.vadere.util.geometry.mesh.impl.PHalfEdge; import org.vadere.util.geometry.mesh.impl.PMesh; +import org.vadere.util.geometry.mesh.impl.PVertex; import org.vadere.util.geometry.mesh.inter.IMesh; import org.vadere.util.geometry.shapes.VPoint; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -22,11 +24,11 @@ public class TestFace { * Building a geometry containing 2 triangles * xyz and wyx */ - private IMesh, PFace> mesh; + private IMesh, PHalfEdge, PFace> mesh; private PFace face1; private PFace face2; private PFace border; - private VPoint x, y, z, w; + private PVertex x, y, z, w; private PHalfEdge zx ; private PHalfEdge xy; private PHalfEdge yz; @@ -48,8 +50,11 @@ public class TestFace { z = mesh.insertVertex(1.5,3.0); zx = mesh.createEdge(x, face1); + mesh.setEdge(x, zx); xy = mesh.createEdge(y, face1); + mesh.setEdge(y, xy); yz = mesh.createEdge(z, face1); + mesh.setEdge(z, yz); mesh.setNext(zx, xy); mesh.setNext(xy, yz); @@ -83,6 +88,7 @@ public class TestFace { wx = mesh.createEdge(x, border); yw = mesh.createEdge(w, border); + mesh.setEdge(w, yw); mesh.setEdge(border, wx); mesh.setTwin(xw, wx); @@ -104,14 +110,19 @@ public class TestFace { @Test public void testPointIterator() { - assertEquals(Arrays.asList(y, z, x), mesh.getVertices(face1)); + assertEquals(new ArrayList(Arrays.asList(y, z, x)), mesh.getVertices(face1)); + } + + @Test + public void testEdgeOfVertex() { + mesh.streamEdges().forEach(edge -> assertEquals(mesh.getVertex(edge), mesh.getVertex(mesh.getEdge(mesh.getVertex(edge))))); } @Test public void testEdgeIterator() { - List adjacentVertices = mesh.getAdjacentVertices(zx); - Set neighbours = new HashSet<>(adjacentVertices); - Set expectedNeighbours = new HashSet<>(); + List> adjacentVertices = mesh.getAdjacentVertices(zx); + Set> neighbours = new HashSet<>(adjacentVertices); + Set> expectedNeighbours = new HashSet<>(); expectedNeighbours.add(z); expectedNeighbours.add(y); expectedNeighbours.add(w); diff --git a/VadereUtils/tests/org/vadere/util/geometry/TestSimplePointLocation.java b/VadereUtils/tests/org/vadere/util/geometry/TestSimplePointLocation.java new file mode 100644 index 0000000000000000000000000000000000000000..660304665647cd0d643dca89f9eef6fe1867fad3 --- /dev/null +++ b/VadereUtils/tests/org/vadere/util/geometry/TestSimplePointLocation.java @@ -0,0 +1,116 @@ +package org.vadere.util.geometry; + +import org.junit.Before; +import org.junit.Test; +import org.vadere.util.geometry.mesh.impl.PFace; +import org.vadere.util.geometry.mesh.impl.PHalfEdge; +import org.vadere.util.geometry.mesh.impl.PMesh; +import org.vadere.util.geometry.mesh.impl.PVertex; +import org.vadere.util.geometry.mesh.inter.IMesh; +import org.vadere.util.triangulation.PointLocation; +import org.vadere.util.geometry.shapes.VPoint; + +import java.util.Arrays; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; + +/** + * Created by bzoennchen on 15.11.16. + */ +public class TestSimplePointLocation { + + private static PFace face1; + private static PFace face2; + private static PFace border; + private static double EPSILON = 1.0e-10; + private IMesh, PHalfEdge, PFace> mesh; + + @Before + public void setUp() throws Exception { + mesh = new PMesh<>((x, y) -> new VPoint(x, y)); + face1 = mesh.createFace(); + face2 = mesh.createFace(); + border = mesh.createFace(true); + + + PVertex x = mesh.createVertex(0,0); + PVertex y = mesh.createVertex(3,0); + PVertex z = mesh.createVertex(1.5,3.0); + PVertex w = mesh.createVertex(4.5,3.0); + + + PHalfEdge zx = mesh.createEdge(x, face1); + PHalfEdge xz = mesh.createEdge(z, border); + mesh.setTwin(zx, xz); + mesh.setEdge(x, zx); + + + PHalfEdge xy = mesh.createEdge(y, face1); + PHalfEdge yx = mesh.createEdge(x, border); + mesh.setTwin(xy, yx); + mesh.setEdge(y, xy); + + + PHalfEdge yz = mesh.createEdge(z, face1); + PHalfEdge zy = mesh.createEdge(y, face2); + mesh.setTwin(zy, yz); + mesh.setEdge(z, yz); + + + PHalfEdge yw = mesh.createEdge(w, face2); + PHalfEdge wy = mesh.createEdge(y, border); + mesh.setTwin(yw, wy); + mesh.setEdge(w, yw); + + PHalfEdge wz = mesh.createEdge(z, face2); + PHalfEdge zw = mesh.createEdge(w, face2); + mesh.setTwin(wz, zw); + + mesh.setNext(zy, yw); + mesh.setNext(yw, wz); + mesh.setNext(wz, zy); + + mesh.setEdge(face2, zy); + + mesh.setNext(zx, xy); + mesh.setNext(xy, yz); + mesh.setNext(yz, zx); + + + mesh.setNext(xz, zw); + mesh.setNext(zw, wy); + mesh.setNext(wy, yx); + mesh.setNext(yx, xz); + + mesh.setEdge(face1, zx); + + mesh.setEdge(border, xz); + } + + @Test + public void testFaceIterator() { + PointLocation pointLocation = new PointLocation<>(Arrays.asList(face1, face2), mesh); + + assertEquals(face1, pointLocation.getFace(new VPoint(0,0)).get()); + + assertEquals(face1, pointLocation.getFace(new VPoint(1.4,1.5)).get()); + + assertEquals(face1, pointLocation.getFace(new VPoint(1.4,1.5)).get()); + + assertEquals(Optional.empty(), pointLocation.getFace(new VPoint(1.4,3.5))); + + assertEquals(Optional.empty(), pointLocation.getFace(new VPoint(-1.5,1.4))); + + assertEquals(face2, pointLocation.getFace(new VPoint(3.5,1.4)).get()); + + assertEquals(Optional.empty(), pointLocation.getFace(new VPoint(3.5,0.2))); + + assertEquals(face2, pointLocation.getFace(new VPoint(3.0,1.5)).get()); + + // edges + assertEquals(face2, pointLocation.getFace(new VPoint(3.0, EPSILON)).get()); + assertEquals(face1, pointLocation.getFace(new VPoint(1.5,3.0 - EPSILON)).get()); + assertEquals(Optional.empty(), pointLocation.getFace(new VPoint(1.5 - EPSILON,3.0 + EPSILON))); + } +} diff --git a/VadereUtils/tests/org/vadere/util/triangulation/TestBoyerWatson.java b/VadereUtils/tests/org/vadere/util/triangulation/TestBoyerWatson.java index 22971543c4c6cf8be7f3a2c1caa4646043ec4e16..b57a73bebf1a3f8729600baebda0488f95e543be 100644 --- a/VadereUtils/tests/org/vadere/util/triangulation/TestBoyerWatson.java +++ b/VadereUtils/tests/org/vadere/util/triangulation/TestBoyerWatson.java @@ -1,16 +1,22 @@ package org.vadere.util.triangulation; +import com.vividsolutions.jts.geom.Coordinate; +import com.vividsolutions.jts.geom.GeometryFactory; +import com.vividsolutions.jts.triangulate.DelaunayTriangulationBuilder; + import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.junit.Before; import org.junit.Test; import org.vadere.util.geometry.mesh.impl.PFace; -import org.vadere.util.geometry.mesh.inter.IMesh; +import org.vadere.util.geometry.mesh.impl.PVertex; import org.vadere.util.geometry.mesh.impl.PHalfEdge; import org.vadere.util.geometry.mesh.inter.IPointLocator; import org.vadere.util.geometry.mesh.inter.ITriangulation; import org.vadere.util.geometry.shapes.VPoint; +import org.vadere.util.geometry.shapes.VRectangle; import org.vadere.util.geometry.shapes.VTriangle; +import org.vadere.util.voronoi.VoronoiDiagram; import java.util.ArrayList; import java.util.Arrays; @@ -21,19 +27,18 @@ import java.util.Random; import java.util.Set; import java.util.stream.Collectors; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class TestBoyerWatson { - private IMesh, PFace> mesh; - private static Logger log = LogManager.getLogger(TestBoyerWatson.class); @Before public void setUp() throws Exception {} @Test - public void testFaceIterator() { + public void testDifferentPointLocator() { VPoint p1 = new VPoint(0, 0); VPoint p2 = new VPoint(50, 0); VPoint p3 = new VPoint(50, 50); @@ -51,13 +56,13 @@ public class TestBoyerWatson { points.add(p5); - List, PFace>> triangulationList = new ArrayList<>(); + List, PHalfEdge, PFace>> triangulationList = new ArrayList<>(); triangulationList.add(ITriangulation.createPTriangulation(IPointLocator.Type.BASE, points, (x, y) -> new VPoint(x, y))); triangulationList.add(ITriangulation.createPTriangulation(IPointLocator.Type.DELAUNAY_TREE, points, (x, y) -> new VPoint(x, y))); //triangulationList.add(ITriangulation.createPTriangulation(IPointLocator.Type.DELAUNAY_HIERARCHY, points, (x, y) -> new VPoint(x, y))); - for(ITriangulation, PFace> delaunayTriangulation : triangulationList) { + for(ITriangulation, PHalfEdge, PFace> delaunayTriangulation : triangulationList) { delaunayTriangulation.finalize(); Set triangulation = delaunayTriangulation.streamTriangles().collect(Collectors.toSet()); @@ -108,9 +113,9 @@ public class TestBoyerWatson { points.add(p2); points.add(p3); - ITriangulation, PFace> delaunayTriangulation = ITriangulation.createPTriangulation(IPointLocator.Type.DELAUNAY_TREE, points, (x, y) -> new VPoint(x, y)); + ITriangulation, PHalfEdge, PFace> delaunayTriangulation = ITriangulation.createPTriangulation(IPointLocator.Type.BASE, points, (x, y) -> new VPoint(x, y)); - PFace face = delaunayTriangulation.locate(centerPoint).get(); + PFace face = delaunayTriangulation.locateFace(centerPoint).get(); delaunayTriangulation.splitTriangle(face, centerPoint); delaunayTriangulation.finalize(); @@ -121,26 +126,45 @@ public class TestBoyerWatson { } @Test - public void testPerformance() { - Set points = new HashSet<>(); + public void testPerformanceForDifferentPointLocators() { + List points = new ArrayList<>(); int width = 300; int height = 300; Random r = new Random(); - - int numberOfPoints = 1000; + assert false; + int numberOfPoints = 5000000; for(int i=0; i< numberOfPoints; i++) { VPoint point = new VPoint(width*r.nextDouble(), height*r.nextDouble()); points.add(point); } + /*Collections.sort(points, (p1, p2) -> { + if(p1.getX() > p2.getX()){ + return -1; + } + else { + return 1; + } + });*/ + + + + long ms = System.currentTimeMillis(); - ITriangulation, PFace> delaunayTriangulation = ITriangulation.createPTriangulation(IPointLocator.Type.DELAUNAY_TREE, points, (x, y) -> new VPoint(x, y)); - delaunayTriangulation.finalize(); + ITriangulation, PHalfEdge, PFace> delaunayTriangulation = ITriangulation.createPTriangulation(IPointLocator.Type.DELAUNAY_HIERARCHY, points, (x, y) -> new VPoint(x, y)); + //delaunayTriangulation.finalize(); + log.info("runtime of the BowyerWatson for " + numberOfPoints + " vertices =" + (System.currentTimeMillis() - ms) + " using the delaunay-tree"); + VoronoiDiagram voronoiDiagram = new VoronoiDiagram(new VRectangle(0,0,width, height)); + ms = System.currentTimeMillis(); + voronoiDiagram.computeVoronoiDiagram(points); + log.info("runtime of the Sweepline for " + numberOfPoints + " vertices =" + (System.currentTimeMillis() - ms) + " using the vadere-voronoi"); + + log.info("start checking the delaunay property, this can take some time"); - Collection triangles = delaunayTriangulation.streamTriangles().collect(Collectors.toList()); + /*Collection triangles = delaunayTriangulation.streamTriangles().collect(Collectors.toList()); for(VTriangle triangle : triangles) { @@ -150,25 +174,42 @@ public class TestBoyerWatson { assertTrue(t.getPoints().stream().noneMatch(p -> !trianglePoints.contains(p) && triangle.isInCircumscribedCycle(p))); } } - log.info("end checking the delaunay property"); + log.info("end checking the delaunay property");*/ + + log.info("check vertex adjustment"); + /*final IMesh, PHalfEdge, PFace> mesh1 = delaunayTriangulation.getMesh(); + mesh1.streamEdges().forEach(edge -> assertEquals(mesh1.getVertex(edge), mesh1.getVertex(mesh1.getEdge(mesh1.getVertex(edge)))));*/ ms = System.currentTimeMillis(); - delaunayTriangulation = ITriangulation.createPTriangulation(IPointLocator.Type.DELAUNAY_HIERARCHY, points, (x, y) -> new VPoint(x, y)); - delaunayTriangulation.finalize(); + //ITriangulation, PHalfEdge, PFace> delaunayTriangulation = ITriangulation.createPTriangulation(IPointLocator.Type.DELAUNAY_HIERARCHY, points, (x, y) -> new VPoint(x, y)); + //delaunayTriangulation.finalize(); log.info("runtime of the BowyerWatson for " + numberOfPoints + " vertices =" + (System.currentTimeMillis() - ms) + " using the delaunay-hierarchy"); - log.info("start checking the delaunay property, this can take some time"); - triangles = delaunayTriangulation.streamTriangles().collect(Collectors.toList()); + /*log.info("start checking the delaunay property, this can take some time"); + List triangles = delaunayTriangulation.streamTriangles().collect(Collectors.toList()); for(VTriangle triangle : triangles) { List trianglePoints = triangle.getPoints(); for(VTriangle t : triangles) { - assertTrue(t.getPoints().stream().noneMatch(p -> !trianglePoints.contains(p) && triangle.isInCircumscribedCycle(p))); + assertTrue(t.getPoints().stream().noneMatch(p -> !trianglePoints.contains(p) && + triangle.isInCircumscribedCycle(p))); } } - log.info("end checking the delaunay property"); + log.info("end checking the delaunay property");*/ + + log.info("check vertex adjustment"); + /*final IMesh, PHalfEdge, PFace> mesh2 = delaunayTriangulation.getMesh(); + mesh2.streamEdges().forEach(edge -> assertEquals(mesh2.getVertex(edge), mesh2.getVertex(mesh2.getEdge(mesh2.getVertex(edge)))));*/ + + GeometryFactory fact = new GeometryFactory(); + Collection coords = points.stream().map(p -> new Coordinate(p.getX(), p.getY())).collect(Collectors.toList()); + ms = System.currentTimeMillis(); + DelaunayTriangulationBuilder builder = new DelaunayTriangulationBuilder(); + builder.setSites(coords); + builder.getTriangles(fact); + log.info("runtime of the ? for " + numberOfPoints + " vertices =" + (System.currentTimeMillis() - ms) + " using the JTS-Delaunay-Triangulation"); }