Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 4de6f544 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen
Browse files

implementation of a very generic half-edge data structure

parent 9bc26ce4
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;