Commit 87c285d5 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen

implementation of a very generic half-edge data structure

parent 074f4e91
package org.vadere.util.geometry.mesh;
import org.apache.commons.lang3.tuple.Triple;
import org.vadere.util.geometry.mesh.inter.IFace;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VTriangle;
public class DAGElement<P extends IPoint> {
private Face<P> face;
import java.util.List;
public class DAGElement<P extends IPoint, F extends IFace<P>> {
private F face;
private Triple<P, P, P> vertices;
private VTriangle triangle;
public DAGElement(final Face<P> face, final Triple<P, P, P> vertices) {
public DAGElement(final F face, List<P> points) {
P p1 = points.get(0);
P p2 = points.get(1);
P p3 = points.get(2);
this.face = face;
this.vertices = Triple.of(p1, p2, p3);
this.triangle = new VTriangle(new VPoint(p1), new VPoint(p2), new VPoint(p3));
}
public DAGElement(final F face, final Triple<P, P, P> vertices) {
this.face = face;
this.vertices = vertices;
VPoint p1 = new VPoint(vertices.getLeft().getX(), vertices.getLeft().getY());
VPoint p2 = new VPoint(vertices.getMiddle().getX(), vertices.getMiddle().getY());
VPoint p3 = new VPoint(vertices.getRight().getX(), vertices.getRight().getY());
VPoint p1 = new VPoint(vertices.getLeft());
VPoint p2 = new VPoint(vertices.getMiddle());
VPoint p3 = new VPoint(vertices.getRight());
this.triangle = new VTriangle(p1, p2, p3);
}
public Face<P> getFace() {
public F getFace() {
return face;
}
......
package org.vadere.util.geometry.mesh;
import org.vadere.util.geometry.shapes.IPoint;
import java.util.Iterator;
/**
* @author Benedikt Zoennchen
*/
public class EdgeIterator<P extends IPoint, E extends IHalfEdge<P>, F extends IFace<P>> implements Iterator<E> {
private E currentHalfEdge;
private E edge;
private boolean started = false;
private IMesh<P, E, F> mesh;
public EdgeIterator(final IMesh<P, E, F> mesh, final F face){
this.edge = mesh.getEdge(face);
this.currentHalfEdge = edge;
this.mesh = mesh;
}
@Override
public boolean hasNext() {
return currentHalfEdge != null && (!started || !currentHalfEdge.equals(edge));
}
@Override
public E next() {
started = true;
E result = currentHalfEdge;
currentHalfEdge = mesh.getNext(currentHalfEdge);
return result;
}
}
\ No newline at end of file
package org.vadere.util.geometry.mesh;
import org.jetbrains.annotations.NotNull;
import org.vadere.util.geometry.GeometryUtils;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.MLine;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VPolygon;
import org.vadere.util.geometry.shapes.VTriangle;
import java.awt.geom.Path2D;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* A Face is a region of a planar separation of the 2-D space, e.g. the region of a Polygon/Triangle and so on.
*
* @author Benedikt Zoennchen
* @param <P> the type of the coordinates the face uses.
*/
public class Face<P extends IPoint> implements Iterable<PHalfEdge<P>>, IFace<P> {
public static <P extends IPoint> Face<P> of(P x, P y, P z) {
Face superTriangle = new Face();
Face borderFace = new Face(true);
PHalfEdge xy = new PHalfEdge(y, superTriangle);
PHalfEdge yz = new PHalfEdge(z, superTriangle);
PHalfEdge zx = new PHalfEdge(x, superTriangle);
xy.setNext(yz);
yz.setNext(zx);
zx.setNext(xy);
PHalfEdge yx = new PHalfEdge(x, borderFace);
PHalfEdge zy = new PHalfEdge(y, borderFace);
PHalfEdge xz = new PHalfEdge(z, borderFace);
yx.setNext(xz);
xz.setNext(zy);
zy.setNext(yx);
xy.setTwin(yx);
yz.setTwin(zy);
zx.setTwin(xz);
superTriangle.setEdge(xy);
borderFace.setEdge(yx);
return superTriangle;
}
public static <P extends IPoint> Face<P> getBorder(Class<P> p) {
return new Face<>(true);
}
/**
* One of the half-edges bordering this face.
*/
private PHalfEdge<P> edge;
private boolean border;
private boolean destroyed = false;
/**
* Default constructor. To construct a face where you have already some half-edges
* bordering this face.
*
* @param edge one of the half-edges bordering this face.
*/
public Face(@NotNull final PHalfEdge<P> edge) {
this(edge, false);
}
public Face(@NotNull final PHalfEdge<P> edge, boolean border) {
this.border = border;
this.edge = edge;
}
/**
* This constructor can be used for constructing a new face without having
* constructed the bordering half-edges jet.
*/
public Face(boolean border) {
this.border = border;
}
public Face() {
this.border = false;
}
public boolean isBorder() {
return border;
}
public void destroy() {
setEdge(null);
destroyed = true;
}
public void toBorder() {
border = true;
}
/**
* Sets one of the half-edges bordering this face.
*
* @param edge half-edge bordering this face
*/
public void setEdge(final PHalfEdge<P> edge) {
this.edge = edge;
}
public PHalfEdge<P> getEdge() {
return edge;
}
public boolean isDestroyed() {
return destroyed;
}
/**
* Computes the area of this face.
*
* @return the area of this face
*/
public double getArea() {
return GeometryUtils.areaOfPolygon(getPoints());
}
/**
*
* @return
*/
public List<P> getPoints() {
return streamPoints().collect(Collectors.toList());
}
/**
* Returns true if and only if the point contained in this face.
*
* @param point the point which might be contained
* @return true if and only if the point contained in this face.
*/
public boolean contains(final P point) {
return toPolygon().contains(point);
}
/**
* Transforms this face into a Polygon object.
*
* @return the Polygon object defined by this face
*/
public VPolygon toPolygon() {
Path2D path2D = new Path2D.Double();
path2D.moveTo(edge.getPrevious().getEnd().getX(), edge.getPrevious().getEnd().getY());
for(PHalfEdge edge : this) {
path2D.lineTo(edge.getEnd().getX(), edge.getEnd().getY());
}
return new VPolygon(path2D);
}
/**
* Transforms this face into a triangle. Assumption: The face is a valid triangle.
*
* @throws IllegalArgumentException if the face does not define a valid triangle
* @return a triangle which is defined by this face
*/
public VTriangle toTriangle() {
List<PHalfEdge<P>> edges = getEdges();
if(edges.size() != 3) {
throw new IllegalArgumentException("this face is not a feasible triangle.");
}
else {
VPoint p1 = new VPoint(edges.get(0).getEnd().getX(), edges.get(0).getEnd().getY());
VPoint p2 = new VPoint(edges.get(1).getEnd().getX(), edges.get(1).getEnd().getY());
VPoint p3 = new VPoint(edges.get(2).getEnd().getX(), edges.get(2).getEnd().getY());
return new VTriangle(p1, p2, p3);
}
}
@Override
public Iterator<PHalfEdge<P>> iterator() {
return new HalfEdgeIterator();
}
public Stream<PHalfEdge<P>> stream () {
Iterable<PHalfEdge<P>> iterable = () -> iterator();
return StreamSupport.stream(iterable.spliterator(), false);
}
public List<PHalfEdge<P>> getEdges() {
return stream().collect(Collectors.toList());
}
public Stream<MLine<P>> streamLines() {
return stream().map(halfEdge -> new MLine(halfEdge.getPrevious().getEnd(), halfEdge.getEnd()));
}
public Stream<P> streamPoints() {
return stream().map(edge -> edge.getEnd());
}
@Override
public String toString() {
return stream().map(edge -> edge.getEnd().toString()).reduce("", (s1, s2) -> s1 + " " + s2);
}
private class HalfEdgeIterator implements Iterator<PHalfEdge<P>> {
private PHalfEdge<P> currentHalfEdge;
private boolean started = false;
private HalfEdgeIterator(){
this.currentHalfEdge = edge;
}
@Override
public boolean hasNext() {
return currentHalfEdge != null && (!started || !currentHalfEdge.equals(edge));
}
@Override
public PHalfEdge<P> next() {
started = true;
PHalfEdge result = currentHalfEdge;
currentHalfEdge = currentHalfEdge.getNext();
return result;
}
}
}
package org.vadere.util.geometry.mesh;
import org.vadere.util.geometry.shapes.IPoint;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.function.Predicate;
/**
* @author Benedikt Zoennchen
*
* @param <P>
*/
public class FaceIterator<P extends IPoint> implements Iterator<Face<P>> {
private LinkedList<Face<P>> facesToVisit;
private Set<Face<P>> visitedFaces;
private Predicate<Face<P>> facePredicate;
public FaceIterator(final Face<P> face, final Predicate<Face<P>> facePredicate) {
facesToVisit = new LinkedList<>();
Face<P> startFace = face.isBorder() ? face.getEdge().getTwin().getFace() : face;
if(startFace.isDestroyed()) {
throw new IllegalArgumentException("this face is already destroyed.");
}
facesToVisit.add(startFace);
visitedFaces = new HashSet<>();
this.facePredicate = facePredicate;
}
public FaceIterator(final Face<P> face) {
this(face, f -> true);
}
@Override
public boolean hasNext() {
return !facesToVisit.isEmpty();
}
@Override
public Face<P> next() {
Face<P> nextFace = facesToVisit.removeFirst();
visitedFaces.add(nextFace);
for(PHalfEdge<P> he : nextFace) {
Face<P> twinFace = he.getTwin().getFace();
if(twinFace.isBorder() || twinFace.isDestroyed() || !facePredicate.test(twinFace)) {
visitedFaces.add(twinFace);
}
if(!visitedFaces.contains(twinFace)) {
facesToVisit.add(twinFace);
}
visitedFaces.add(twinFace);
}
return nextFace;
}
}
package org.vadere.util.geometry.mesh;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VPolygon;
import java.awt.geom.Path2D;
/**
* @author Benedikt Zoennchen
* @param <P>
*/
public interface IFace<P extends IPoint> {
}
package org.vadere.util.geometry.mesh;
import org.vadere.util.geometry.shapes.IPoint;
public interface IHalfEdge<P extends IPoint> {}
package org.vadere.util.geometry.mesh;
import org.apache.commons.collections.IteratorUtils;
import org.jetbrains.annotations.NotNull;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VPolygon;
import java.awt.geom.Path2D;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
/**
* @author Benedikt Zoennchen
* @param <P>
*/
public interface IMesh<P extends IPoint, E extends IHalfEdge<P>, F extends IFace<P>> extends Iterable<F> {
E getNext(@NotNull E halfEdge);
E getPrev(@NotNull E halfEdge);
E getTwin(@NotNull E halfEdge);
F getFace(@NotNull E halfEdge);
E getEdge(@NotNull P vertex);
E getEdge(@NotNull F face);
P getVertex(@NotNull E halfEdge);
default F getTwinFace(@NotNull E halfEdge) {
return getFace(getTwin(halfEdge));
}
boolean isBoundary(@NotNull F face);
boolean isBoundary(@NotNull E halfEdge);
boolean isDestroyed(@NotNull F face);
void setTwin(@NotNull E halfEdge, @NotNull E twin);
void setNext(@NotNull E halfEdge, @NotNull E next);
void setPrev(@NotNull E halfEdge, @NotNull E prev);
void setFace(@NotNull E halfEdge, @NotNull F face);
void setEdge(@NotNull F face, @NotNull E edge);
void setEdge(@NotNull P vertex, @NotNull E edge);
void setVertex(@NotNull E halfEdge, @NotNull P vertex);
List<E> getEdges(@NotNull P vertex);
default List<E> getEdges(@NotNull F face) {
return IteratorUtils.toList(new EdgeIterator(this, face));
}
default List<F> getFaces(@NotNull E edge) { return IteratorUtils.toList(new NeighbourFaceIterator(this, edge)); }
default List<E> getNeighbours(@NotNull E edge) { return IteratorUtils.toList(new NeighbourIterator(this, edge)); }
default Iterable<E> getNeighbourIt(E edge) {
return () -> new NeighbourIterator(this, edge);
}
default Iterable<E> getEdgeIt(F face) {
return () -> new EdgeIterator(this, face);
}
default Iterable<F> getIncidentFacesIt(@NotNull E edge) { return () -> new NeighbourFaceIterator<>(this, edge); }
E createEdge(@NotNull P vertex);
E createEdge(@NotNull P vertex, @NotNull F face);
F createFace();
void destroyFace(@NotNull F face);
void destroyEdge(@NotNull E edge);
List<F> getFaces();
@Override
default Iterator<F> iterator() {
return getFaces().iterator();
}
default VPolygon toPolygon(F face) {
Path2D path2D = new Path2D.Double();
E edge = getEdge(face);
E prev = getPrev(edge);
path2D.moveTo(getVertex(prev).getX(), getVertex(prev).getY());
path2D.lineTo(getVertex(edge).getX(), getVertex(edge).getY());
while (!edge.equals(prev)) {
edge = getNext(edge);
P p = getVertex(edge);
path2D.lineTo(p.getX(), p.getY());
}
return new VPolygon(path2D);
}
default Optional<F> locate(final double x, final double y) {
for(F face : getFaces()) {
VPolygon polygon = toPolygon(face);
if(polygon.contains(new VPoint(x, y))) {
return Optional.of(face);
}
}
return Optional.empty();
}
}
package org.vadere.util.geometry.mesh;
import org.vadere.util.geometry.shapes.IPoint;
import java.util.Iterator;
/**
* This iterator assumes that the this edge is completely surrounded by faces.
*
* @author Benedikt Zoennchen
*/
public
class NeighbourFaceIterator<P extends IPoint, E extends IHalfEdge<P>, F extends IFace<P>> implements Iterator<F> {
private NeighbourIterator<P, E, F> neighbourIterator;
private IMesh<P, E, F> mesh;
public NeighbourFaceIterator(final IMesh<P, E, F> mesh, E edge) {
this.neighbourIterator = new NeighbourIterator<>(mesh, edge);
this.mesh = mesh;
}
@Override
public boolean hasNext() {
return neighbourIterator.hasNext();
}
@Override
public F next() {
return mesh.getFace(neighbourIterator.next());
}
}
\ No newline at end of file
package org.vadere.util.geometry.mesh;
import org.vadere.util.geometry.shapes.IPoint;
import java.util.Iterator;
/**
* @author Benedikt Zoennchen
* @param <P>
* @param <E>
* @param <F>
*/
public class NeighbourIterator<P extends IPoint, E extends IHalfEdge<P>, F extends IFace<P>> implements Iterator<E> {
private IMesh<P, E, F> mesh;
private E current;
private E edge;
private boolean first;
public NeighbourIterator(final IMesh<P, E, F> mesh, final E edge) {
this.mesh = mesh;
this.edge = edge;
this.current = mesh.getNext(edge);
this.first = true;
}
@Override
public boolean hasNext() {
return (first || current != mesh.getNext(edge));
}
@Override
public E next() {
E result = current;
current = mesh.getNext(mesh.getTwin(result));
first = false;
return result;
}
}
package org.vadere.util.geometry.mesh;
import org.jetbrains.annotations.NotNull;
import org.vadere.util.geometry.shapes.IPoint;
import java.util.List;
/**
* @author Benedikt Zoennchen
*/
public class PMesh<P extends IPoint> implements IMesh<P, PHalfEdge<P>, Face<P>> {
public PMesh() {}
@Override
public PHalfEdge<P> getNext(final PHalfEdge<P> halfEdge) {
return halfEdge.getNext();
}
@Override
public PHalfEdge<P> getPrev(final PHalfEdge<P> halfEdge) {
return halfEdge.getPrevious();
}