Commit 6ac97cb1 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen
Browse files

ffm bug fix, add some mesh iterators

parent 7fcd5216
......@@ -36,6 +36,22 @@ public class GeometryUtils {
return result;
}
public static double derterminant2D(double x1, double y1, double x2, double y2) {
return x1 * y2 - y1 * x2;
}
//http://mathworld.wolfram.com/Line-LineIntersection.html
public static VPoint intersectionPoint(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
double a = derterminant2D(x1, x2, y1, y2);
double b = derterminant2D(x3, x4, y3, y4);
double c = derterminant2D(x1-x2, x3 - x4, y1 - y2, y3 - y4);
double x = derterminant2D(a, b, x1 - x2, x3 - x4) / c;
double y = derterminant2D(a, b, y1 - y2, y3 - y4) / c;
return new VPoint(x,y);
}
public static boolean collectionContains(
Collection<? extends VShape> collection, VPoint point) {
for (VShape shape : collection) {
......
......@@ -10,6 +10,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
......@@ -270,11 +271,6 @@ public class PMesh<P extends IPoint> implements IMesh<P, PVertex<P>, PHalfEdge<P
vertex.destroy();
}
@Override
public Stream<PFace<P>> streamFaces() {
return faces.stream();
}
@Override
public Stream<PHalfEdge<P>> streamEdges() {
return edges.stream();
......@@ -297,4 +293,14 @@ public class PMesh<P extends IPoint> implements IMesh<P, PVertex<P>, PHalfEdge<P
public List<PHalfEdge<P>> getBoundaryEdges() {
return streamEdges().filter(edge -> edge.isBoundary()).filter(edge -> !edge.isDestroyed()).collect(Collectors.toList());
}
@Override
public List<PVertex<P>> getBoundaryVertices() {
return streamEdges().filter(edge -> edge.isBoundary()).filter(edge -> !edge.isDestroyed()).map(edge -> getVertex(edge)).collect(Collectors.toList());
}
@Override
public Stream<PFace<P>> streamFaces(@NotNull Predicate<PFace<P>> predicate) {
return faces.stream().filter(predicate);
}
}
......@@ -9,10 +9,12 @@ import org.vadere.util.geometry.mesh.gen.PFace;
import org.vadere.util.geometry.mesh.gen.PHalfEdge;
import org.vadere.util.geometry.mesh.gen.PMesh;
import org.vadere.util.geometry.mesh.gen.PVertex;
import org.vadere.util.geometry.mesh.iterators.AdjacentVertexIterator;
import org.vadere.util.geometry.mesh.iterators.EdgeIterator;
import org.vadere.util.geometry.mesh.iterators.AdjacentFaceIterator;
import org.vadere.util.geometry.mesh.iterators.EdgeOfVertexIterator;
import org.vadere.util.geometry.mesh.iterators.IncidentEdgeIterator;
import org.vadere.util.geometry.mesh.iterators.PointIterator;
import org.vadere.util.geometry.mesh.iterators.SurroundingFaceIterator;
import org.vadere.util.geometry.mesh.iterators.VertexIterator;
import org.vadere.util.geometry.shapes.IPoint;
......@@ -30,6 +32,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
......@@ -59,6 +62,10 @@ public interface IMesh<P extends IPoint, V extends IVertex<P>, E extends IHalfEd
return new VPoint(new VPoint(vertex));
}
default VPoint toPoint(@NotNull E edge) {
return toPoint(getVertex(edge));
}
E getEdge(@NotNull V vertex);
E getEdge(@NotNull F face);
......@@ -92,6 +99,10 @@ public interface IMesh<P extends IPoint, V extends IVertex<P>, E extends IHalfEd
*/
boolean isBoundary(@NotNull F face);
default boolean isAtBoundary(@NotNull E edge) {
return isBoundary(edge) || isBoundary(getTwin(edge));
}
default boolean isNeighbourBoundary(@NotNull F face){
for(F neighbourFace : getFaceIt(face)) {
if(isBoundary(neighbourFace)) {
......@@ -209,7 +220,13 @@ public interface IMesh<P extends IPoint, V extends IVertex<P>, E extends IHalfEd
List<E> getBoundaryEdges();
Stream<F> streamFaces();
List<V> getBoundaryVertices();
Stream<F> streamFaces(@NotNull final Predicate<F> predicate);
default Stream<F> streamFaces() {
return streamFaces(f -> true);
}
Stream<E> streamEdges();
......@@ -281,7 +298,7 @@ public interface IMesh<P extends IPoint, V extends IVertex<P>, E extends IHalfEd
default List<F> getFaces(@NotNull V vertex) { return IteratorUtils.toList(new AdjacentFaceIterator(this, getEdge(vertex))); }
/**
* Returns a Iterable which can be used to iterate over all edges which are adjacent to the vertex of this edge.
* Returns a Iterable which can be used to iterate over all edges which end point is the vertex that is adjacent to the vertex of this edge.
*
* @param edge the edge which holds the vertex
* @return a Iterable which can be used to iterate over all edges which are adjacent to the vertex of this edge.
......@@ -290,6 +307,16 @@ public interface IMesh<P extends IPoint, V extends IVertex<P>, E extends IHalfEd
return () -> new IncidentEdgeIterator(this, edge);
}
/**
* Returns a Iterable which can be used to iterate over adjacent vertices of this vertex.
*
* @param vertex the vertex
* @return a Iterable which can be used to iterate over all adjacent vertices.
*/
default Iterable<V> getAdjacentVertexIt(@NotNull final V vertex) {
return () -> new AdjacentVertexIterator<>(this, vertex);
}
/**
* Returns an Iterable which can be used to iterate over all edges of a face.
*
......@@ -300,6 +327,16 @@ public interface IMesh<P extends IPoint, V extends IVertex<P>, E extends IHalfEd
return () -> new EdgeIterator<>(this, face);
}
/**
* Returns an Iterable which can be used to iterate over all edges of a face which the edge is part of.
*
* @param edge the edge which is part of the face the iterable iterates over
* @return an Iterable which can be used to iterate over all edges of a face.
*/
default Iterable<E> getEdgeIt(E edge) {
return () -> new EdgeIterator<>(this, edge);
}
/**
* Returns an Iterable which can be used to iterate over all vertices of a face.
*
......@@ -310,6 +347,16 @@ public interface IMesh<P extends IPoint, V extends IVertex<P>, E extends IHalfEd
return () -> new VertexIterator<>(this, face);
}
/**
* Returns an Iterable which can be used to iterate over all vertices of a face.
*
* @param face the face the iterable iterates over
* @return an Iterable which can be used to iterate over all vertices of a face.
*/
default Iterable<P> getPointIt(F face) {
return () -> new PointIterator<>(this, face);
}
/**
* Returns a Stream of edges of a face.
*
......@@ -321,6 +368,17 @@ public interface IMesh<P extends IPoint, V extends IVertex<P>, E extends IHalfEd
return StreamSupport.stream(iterable.spliterator(), false);
}
/**
* Returns a Stream of edges of a face.
*
* @param edge the edge of the face of which edges the stream consist.
* @return a Stream of edges of a face specified by the edge.
*/
default Stream<E> streamEdges(E edge) {
Iterable<E> iterable = getEdgeIt(edge);
return StreamSupport.stream(iterable.spliterator(), false);
}
/**
* Returns a Stream of vertices of a face.
*
......@@ -374,6 +432,14 @@ public interface IMesh<P extends IPoint, V extends IVertex<P>, E extends IHalfEd
*/
default Iterable<F> getAdjacentFacesIt(@NotNull E edge) { return () -> new AdjacentFaceIterator<>(this, edge); }
/**
* Returns an Iterable which can be used to iterate over all faces which are adjacent to the vertex.
*
* @param vertex the vertex
* @return an Iterable which can be used to iterate over all faces which are adjacent to the vertex
*/
default Iterable<F> getAdjacentFacesIt(@NotNull V vertex) { return () -> new AdjacentFaceIterator<>(this, getEdge(vertex)); }
/**
* Returns a List of all faces which are adjacent to the vertex of the edge
*
......
......@@ -279,7 +279,6 @@ public interface IPolyConnectivity<P extends IPoint, V extends IVertex<P>, E ext
}
default boolean contains(final double x1, final double y1, final F face) {
assert !getMesh().isBoundary(face);
return getMesh().streamEdges(face).noneMatch(edge -> isRightOf(x1, y1, edge));
}
......
......@@ -4,15 +4,16 @@ 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.Vector2D;
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.VTriangle;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.function.Predicate;
//TODO: check unused methods!
/**
* @author Benedikt Zoennchen
*
......@@ -316,8 +317,6 @@ public interface ITriConnectivity<P extends IPoint, V extends IVertex<P>, E exte
}
// TODO: maybe without if, just do it? its faster?
assert mesh.getVertex(b2) == va0;
assert mesh.getVertex(a2) == vb0;
......@@ -361,7 +360,7 @@ public interface ITriConnectivity<P extends IPoint, V extends IVertex<P>, E exte
*
* @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).
* @param legalize true means that recursive filps will be done afterwards to legalizeRecursively illegal edges (e.g. delaunay requirement).
*
* @return an half-edge which has point as its end-point
*/
......@@ -455,7 +454,7 @@ public interface ITriConnectivity<P extends IPoint, V extends IVertex<P>, E exte
*
* @param edge an edge zx of a triangle xyz
*/
/*default void legalize(@NotNull final E edge, final V p, int depth) {
default void legalizeRecursively(@NotNull final E edge, final V p, int depth) {
if(isIllegal(edge, p)) {
assert isFlipOk(edge);
assert getMesh().getVertex(getMesh().getNext(edge)).equals(p);
......@@ -472,19 +471,19 @@ public interface ITriConnectivity<P extends IPoint, V extends IVertex<P>, E exte
E e2 = getMesh().getNext(getMesh().getTwin(edge));
legalize(e1, p, depth+1);
legalize(e2, p, depth+1);
legalizeRecursively(e1, p, depth+1);
legalizeRecursively(e2, p, depth+1);
}
else {
E e1 = getMesh().getNext(edge);
E e2 = getMesh().getPrev(getMesh().getTwin(edge));
legalize(e1, p, depth+1);
legalize(e2, p, depth+1);
legalizeRecursively(e1, p, depth+1);
legalizeRecursively(e2, p, depth+1);
}
}
}*/
}
void legalizeNonRecursive(final E edge, final V p);
......@@ -540,12 +539,14 @@ public interface ITriConnectivity<P extends IPoint, V extends IVertex<P>, E exte
optFace = Optional.empty();
}
if(optFace.isPresent() && getMesh().isBoundary(optFace.get())) {
return optFace;
/*if(optFace.isPresent() && getMesh().isBoundary(optFace.get())) {
return Optional.empty();
}
else {
return optFace;
}
}*/
}
/*default Optional<P> locateVertex(double x, double y, F startFace) {
......@@ -605,77 +606,44 @@ public interface ITriConnectivity<P extends IPoint, V extends IVertex<P>, E exte
}
}
/**
* 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 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, F startFace) {
//assert !getMesh().isBoundary(startFace);
//assert contains(startFace, x1, y1);
VTriangle triangle = getMesh().toTriangle(startFace);
VPoint incenter = triangle.getIncenter();
// TODO: still required?
default E walkThroughHole(@NotNull VPoint q, @NotNull VPoint p, @NotNull E enteringEdge) {
assert GeometryUtils.intersectLine(q, p, getMesh().getPoint(enteringEdge), getMesh().getPoint(getMesh().getPrev(enteringEdge)));
E next = getMesh().getNext(enteringEdge);
double x2 = incenter.getX();
double y2 = incenter.getY();
E entryEdge = null;
F face = startFace;
boolean found = true;
//TODO: this seems to be very slow!
while(found && !contains(x1, y1, face)) {
found = false;
for(E halfEdge : getMesh().getEdgeIt(face)) {
if(!halfEdge.equals(entryEdge)) {
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);
found = true;
break;
}
}
while (enteringEdge != next) {
VPoint p1 = getMesh().toPoint(getMesh().getVertex(enteringEdge));
VPoint p2 = getMesh().toPoint(getMesh().getVertex(getMesh().getPrev(enteringEdge)));
if(GeometryUtils.intersectLine(q, p, p1, p2)) {
return next;
}
}
//assert found || getMesh().isBoundary(face);
next = getMesh().getNext(next);
}
return face;
throw new IllegalArgumentException("no second intersection edge fount");
}
default F walkThroughHole(double x1, double y1, @NotNull F borderFace) {
assert getMesh().isBoundary(borderFace);
double minDistance = Double.MAX_VALUE;
F choosenFace = borderFace;
for(E edge : getMesh().getEdgeIt(borderFace)) {
V v1 = getMesh().getVertex(getMesh().getPrev(edge));
V v2 = getMesh().getVertex(edge);
// get out of the hole/border. Note: non-border faces are ccw oriented but border-faces are cw oriented!
if (GeometryUtils.isRightOf(v2, v1, x1, y1)) {
// TODO: still required?
default Optional<E> findIntersectionEdge(final double x1, final double y1, final V startVertex) {
VPoint q = getMesh().toPoint(startVertex);
VPoint p = new VPoint(x1, y1);
double distance = GeometryUtils.distanceToLineSegment(v1, v2, x1, y1);
E edge = getMesh().getEdge(startVertex);
E candidate = getMesh().getPrev(edge);
E next = getMesh().getTwin(getMesh().getNext(edge));
if(distance < minDistance) {
minDistance = distance;
choosenFace = getMesh().getTwinFace(edge);
}
do {
candidate = getMesh().isBoundary(candidate) ? getMesh().getTwin(candidate) : candidate;
if(isRightOf(x1, y1, candidate) && intersects(q, p, candidate)) {
return Optional.of(candidate);
}
}
return choosenFace;
candidate = getMesh().getPrev(next);
next = getMesh().getTwin(getMesh().getNext(next));
} while (edge != next);
return Optional.empty();
}
/**
......@@ -689,59 +657,100 @@ public interface ITriConnectivity<P extends IPoint, V extends IVertex<P>, E exte
* @return
*/
default F straightWalk2D(final double x1, final double y1, final F startFace) {
VPoint q = getMesh().toTriangle(startFace).getIncenter();
VPoint p = new VPoint(x1, y1);
return straightWalk2D(x1, y1, startFace, e -> !isRightOf(x1, y1, e));
}
if(q.getX() == x1 && q.getY() == y1) {
return startFace;
/**
* Marching from a vertex which is the vertex of startVertex towards direction until the stopCondition is fulfilled.
*
* @param startVertex
* @param direction
* @param stopCondition
* @return
*/
default F straightWalk2D(final E startVertex, final VPoint direction, final Predicate<E> stopCondition) {
E startEdge = getMesh().getPrev(startVertex);
VPoint p1 = getMesh().toPoint(startVertex);
VPoint p2 = p1.add(direction);
VPoint p3 = getMesh().toPoint(startEdge);
VPoint p4 = getMesh().toPoint(getMesh().getPrev(startEdge));
VPoint q = p1;
VPoint p = GeometryUtils.intersectionPoint(p1.getX(), p1.getY(), p2.getX(), p2.getY(), p3.getX(), p3.getY(), p4.getX(), p4.getY());
F face;
E edge;
if(isRightOf(p.getX(), p.getY(), startEdge)) {
edge = startEdge;
face = getMesh().getFace(edge);
}
else {
edge = getMesh().getTwin(startEdge);
face = getMesh().getFace(edge);
}
E intersectionEdge = getMesh().getTwin(getMesh()
.streamEdges(startFace)
.filter(edge -> intersects(q, p, edge)).findAny().get());
return straightWalk2D(q, p, face, edge, stopCondition);
}
F face = getMesh().getFace(intersectionEdge);
default F straightWalk2D(final double x1, final double y1, final F startFace, final Predicate<E> stopCondition) {
assert !getMesh().isBoundary(startFace);
while (!contains(x1, y1, face)) {
// initialize
F face = startFace;
E edge = getMesh().getEdge(face);
VPoint q = getMesh().toTriangle(startFace).getIncenter(); // walk from q to p
VPoint p = new VPoint(x1, y1);
// default case i.e. checking a triangle
if(!getMesh().isBoundary(face)) {
E e1 = getMesh().getNext(intersectionEdge);
E e2 = getMesh().getNext(e1);
return straightWalk2D(q, p, face, edge, stopCondition);
}
if(intersects(q, p, e1)){
intersectionEdge = getMesh().getTwin(e1);
face = getMesh().getFace(intersectionEdge);
}
else if(intersects(q, p, e2)) {
intersectionEdge = getMesh().getTwin(e2);
face = getMesh().getFace(intersectionEdge);
}
else {
if(isMember(face, x1, y1)) {
return face;
default F straightWalk2D(final VPoint q, final VPoint p, final F startFace, final E startEdge, final Predicate<E> stopCondition) {
Optional<E> optEdge;
F face = startFace;
E edge = startEdge;
do {
optEdge = getMesh().streamEdges(edge).filter(e -> !stopCondition.test(e) && intersects(q, p, e)).findAny();
if(optEdge.isPresent()) {
edge = getMesh().getTwin(optEdge.get());
face = getMesh().getTwinFace(optEdge.get());
// special case: hitting the boundary
if(getMesh().isBoundary(face)) {
VPoint p1 = getMesh().toPoint(getMesh().getVertex(edge));
optEdge = getMesh().streamEdges(edge).filter(e -> !stopCondition.test(e) && intersects(q, p, e)).findAny();
if(optEdge.isPresent()) {
VPoint p2 = getMesh().toPoint(getMesh().getVertex(optEdge.get()));
if(p.distance(p1) <= p.distance(p2)) {
return face;
}
}
else {
intersects(q, p, e1);
intersects(q, p, e2);
throw new IllegalArgumentException(e1 + " and " + e2 + " do not intersect " + q + "->" + p);
return face;
}
}
} // non-default case checking a convex! polygon.
else {
final E enteringEdge = intersectionEdge;
intersectionEdge = getMesh().getTwin(getMesh()
.streamEdges(face)
.filter(edge -> getMesh().getTwin(edge) != enteringEdge)
.filter(edge -> intersects(q, p, edge))
.findAny().get());
face = getMesh().getFace(intersectionEdge);
}
}
} while (optEdge.isPresent());
return face;
}
default boolean isOuterBoundary(final F face) {
if(getMesh().isBoundary(face)) {
E boundaryEdge = getMesh().getEdge(face);
VPoint p = getMesh().toPoint(getMesh().getVertex(getMesh().getNext(boundaryEdge)));
return isRightOf(p.getX(), p.getY(), boundaryEdge);
}
else {
return false;
}
}
/**
* Marching to the face which triangleContains the point defined by (x2, y2). Inside the startFace.
* This algorithm does NOT works if there are convex polygon (holes) inside the triangulation.
......@@ -761,14 +770,11 @@ public interface ITriConnectivity<P extends IPoint, V extends IVertex<P>, E exte
while (true) {
if(getMesh().isBoundary(face)) {
F tmpFace = walkThroughHole(x1, y1, face);
if(getMesh().isBoundary(tmpFace)) {
assert tmpFace == face;
return tmpFace;
if(contains(x1, y1, face)) {
return face;
}
else {
face = tmpFace;
throw new IllegalArgumentException("marchRandom2D can not walk through holes.");
}
}
......
package org.vadere.util.geometry.mesh.iterators;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.vadere.util.geometry.mesh.inter.IFace;
import org.vadere.util.geometry.mesh.inter.IHalfEdge;
import org.vadere.util.geometry.mesh.inter.IMesh;
import org.vadere.util.geometry.mesh.inter.IVertex;
import org.vadere.util.geometry.shapes.IPoint;
import java.util.Iterator;
/**
* This iterator iterates over the adjacent vertices of the vertex of this iterator.
*
* @author Benedikt Zoennchen