Commit dea61af2 authored by Benedikt Zoennchen's avatar Benedikt Zoennchen
Browse files

implemented the FMM on uniform triangulation

parent f9669e1c
......@@ -56,7 +56,7 @@ public class ProjectViewModel {
Arrays.stream(rows)
.mapToObj(row -> getOutputTableModel().getValue(row))
.filter(dir -> IOOutput.deleteOutputDirectory(dir))
.forEach(dir -> logger.info("delete output directory: " + dir.getName()));
.forEach(dir -> logger.info("deleteEdge output directory: " + dir.getName()));
}
public void deleteScenarios(final int[] rows) {
......
......@@ -351,12 +351,12 @@ public class TopographyWindow extends JPanel {
"insert-copied-element");
getActionMap().put("insert-copied-element", insertCopiedElementAction);
// delete element
// deleteEdge element
TopographyAction deleteElement =
new ActionDeleteElement("delete element", panelModel, undoSupport, basicAction);
new ActionDeleteElement("deleteEdge element", panelModel, undoSupport, basicAction);
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),
"delete-element");
getActionMap().put("delete-element", deleteElement);
"deleteEdge-element");
getActionMap().put("deleteEdge-element", deleteElement);
// undo
getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
......
......@@ -25,7 +25,7 @@ import java.util.Optional;
import java.util.stream.Collectors;
/**
* This IOUtility class provides all methods to load, delete, list, clean output directories.
* This IOUtility class provides all methods to load, deleteEdge, list, clean output directories.
* Each output directory contains two fiels *.scenario and *.trajectories.
*
*/
......@@ -129,7 +129,7 @@ public abstract class IOOutput {
try {
Files.delete(file.toPath());
} catch (IOException e) {
logger.error("could not delete scenario-file: " + file.getAbsolutePath() + ", "
logger.error("could not deleteEdge scenario-file: " + file.getAbsolutePath() + ", "
+ e.getLocalizedMessage());
}
}
......@@ -138,7 +138,7 @@ public abstract class IOOutput {
try {
Files.delete(file.toPath());
} catch (IOException e) {
logger.error("could not delete trajectory-file: " + file.getAbsolutePath() + ", "
logger.error("could not deleteEdge trajectory-file: " + file.getAbsolutePath() + ", "
+ e.getLocalizedMessage());
}
}
......@@ -146,7 +146,7 @@ public abstract class IOOutput {
try {
Files.delete(directory.toPath());
} catch (IOException e) {
logger.error("could not delete output-directory: " + directory.getAbsolutePath() + ", "
logger.error("could not deleteEdge output-directory: " + directory.getAbsolutePath() + ", "
+ e.getLocalizedMessage());
}
......
......@@ -90,15 +90,15 @@ public class Tree {
throws MigrationException {
List<Node> nodes = recursiveScan(root, parentKey, key, caller);
if(nodes.size() > 1) {
throw new MigrationException(caller, "can't automatically delete the unrecognized field [" + key
throw new MigrationException(caller, "can't automatically deleteEdge the unrecognized field [" + key
+ "] because more than one tree-path ends with [" + parentKey + " > " + key + "]");
}
else if(nodes.isEmpty()) {
throw new MigrationException(caller, "can't automatically delete the unrecognized field [" + key
throw new MigrationException(caller, "can't automatically deleteEdge the unrecognized field [" + key
+ "] no tree-path ends with [" + parentKey + " > " + key + "]");
}
else {
log.append("\t- delete unrecognized node [" + key + "] under node "
log.append("\t- deleteEdge unrecognized node [" + key + "] under node "
+ pathToString(getPathToNode(nodes.get(0).parent)) + "\n");
nodes.get(0).delete();
}
......
......@@ -24,6 +24,6 @@ public class DeleteInArrayIncident extends Incident{
public void resolve(@NotNull final Tree tree, @NotNull StringBuilder log) throws MigrationException {
super.stillApplies(tree);
tree.deleteNodeInArray(pathToArray, key);
log.append("\t- delete node [" + key + "] in array " + Tree.pathToString(pathToArray) + "\n");
log.append("\t- deleteEdge node [" + key + "] in array " + Tree.pathToString(pathToArray) + "\n");
}
}
\ No newline at end of file
......@@ -21,7 +21,7 @@ public class DeletionIncident extends Incident {
@Override
public void resolve(Tree graph, StringBuilder log) throws MigrationException {
super.stillApplies(graph);
log.append("\t- delete node " + graph.pathToString(path) + "\n");
log.append("\t- deleteEdge node " + graph.pathToString(path) + "\n");
graph.deleteNode(path);
}
......
......@@ -24,6 +24,17 @@
<directory>resources</directory>
</resource>
</resources>
<testResources>
<testResource>
<directory>testResources</directory>
</testResource>
<testResource>
<directory>tests</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
......
......@@ -156,10 +156,12 @@ public class GeometryUtils {
*/
public static double areaOfPolygon(final List<? extends IPoint> vertices) {
double result = 0;
for (int i = 0; i < vertices.size() - 1; i++) {
result += (vertices.get(i).getY() + vertices.get(i + 1).getY())
* (vertices.get(i).getX() - vertices.get(i + 1).getX());
if(vertices.size() >= 3) {
for (int i = 0; i < vertices.size() - 1; i++) {
result += vertices.get(i).getX() * vertices.get(i + 1).getY() - vertices.get(i + 1).getX() * vertices.get(i).getY();
}
int n = vertices.size() - 1;
result += vertices.get(n).getX() * vertices.get(0).getY() - vertices.get(0).getX() * vertices.get(n).getY();
}
return Math.abs(result) / 2.0;
}
......
......@@ -24,21 +24,37 @@ import java.util.stream.StreamSupport;
*/
public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
public static <P extends IPoint> Face<P> of(P p1, P p2, P p3) {
public static <P extends IPoint> Face<P> of(P x, P y, P z) {
Face superTriangle = new Face();
HalfEdge edge1 = new HalfEdge(p1, superTriangle);
HalfEdge edge2 = new HalfEdge(p2, superTriangle);
HalfEdge edge3 = new HalfEdge(p3, superTriangle);
edge1.setNext(edge2);
edge2.setNext(edge3);
edge3.setNext(edge1);
superTriangle.setEdge(edge1);
Face borderFace = new Face(true);
HalfEdge xy = new HalfEdge(y, superTriangle);
HalfEdge yz = new HalfEdge(z, superTriangle);
HalfEdge zx = new HalfEdge(x, superTriangle);
xy.setNext(yz);
yz.setNext(zx);
zx.setNext(xy);
HalfEdge yx = new HalfEdge(x, borderFace);
HalfEdge zy = new HalfEdge(y, borderFace);
HalfEdge xz = new HalfEdge(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<>();
return new Face<>(true);
}
/**
......@@ -46,6 +62,8 @@ public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
*/
private HalfEdge<P> edge;
private boolean border;
/**
* Default constructor. To construct a face where you have already some half-edges
* bordering this face.
......@@ -53,6 +71,11 @@ public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
* @param edge one of the half-edges bordering this face.
*/
public Face(@NotNull final HalfEdge<P> edge) {
this(edge, false);
}
public Face(@NotNull final HalfEdge<P> edge, boolean border) {
this.border = border;
this.edge = edge;
}
......@@ -60,10 +83,21 @@ public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
* This constructor can be used for constructing a new face without having
* constructed the bordering half-edges jet.
*/
public Face() {}
public Face(boolean border) {
this.border = border;
}
public Face() {
this.border = false;
}
public boolean isBorder() {
return border;
}
/**
* Sets one of the half-edges bordering this face.
*
* @param edge half-edge bordering this face
*/
public void setEdge(@NotNull HalfEdge<P> edge) {
......@@ -76,6 +110,7 @@ public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
/**
* Computes the area of this face.
*
* @return the area of this face
*/
public double getArea() {
......@@ -90,10 +125,21 @@ public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
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());
......@@ -103,6 +149,12 @@ public class Face<P extends IPoint> implements Iterable<HalfEdge<P>> {
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<HalfEdge<P>> edges = getEdges();
if(edges.size() != 3) {
......
package org.vadere.util.geometry.data;
import org.apache.commons.collections.IteratorUtils;
import org.jetbrains.annotations.NotNull;
import org.vadere.util.geometry.shapes.IPoint;
import org.vadere.util.geometry.shapes.VLine;
import org.vadere.util.geometry.shapes.VPoint;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Optional;
import java.util.List;
public class HalfEdge<P extends IPoint> {
public class HalfEdge<P extends IPoint> implements Iterable<HalfEdge<P>> {
/**
* point at the end of the half edge.
......@@ -36,12 +38,10 @@ public class HalfEdge<P extends IPoint> {
*/
private Face<P> face;
private double data;
public HalfEdge (@NotNull final P end, @NotNull final Face<P> face) {
this.end = end;
this.face = face;
this.data = 0.0;
}
public Face getFace() {
......@@ -76,23 +76,133 @@ public class HalfEdge<P extends IPoint> {
return twin;
}
public void setTwin(final @NotNull HalfEdge twin) {
/**
* Deletes the vertex i.e. end point of this halfedge from the geometry by deleting all halfedges
* connected to this vertex.
*/
public void deleteVertex() {
// 1. gather all edges
List<HalfEdge<P>> edges = IteratorUtils.toList(this.iterator());
// 2. delete all edges of connected to the end point
edges.stream().filter(HalfEdge::isValid).forEach(edge -> edge.deleteEdge());
}
/**
* Deletes this edge (and its twin) from the geometry. This operation requires O(n) where
* n is the number of edges inside a face. It may delete an vertex if it has degree = 2.
* Furthermore, faces may be merged. If a face will become invalid it will be the face of this
* edge.
*
* @return true if the operation deletes a vertex i.e. an end point is no longer part of the geometry, false otherwise.
*/
public boolean deleteEdge() {
boolean deleteVertex = false;
// the edge is inside another face.
if(!getFace().isBorder() && !getTwin().getFace().isBorder()) {
// 1. remove one of the 2 faces. We deleteEdge the face of this edge, the twin face survives.
for(HalfEdge<P> halfEdge : this.face) {
halfEdge.setFace(getTwin().getFace());
}
// 2. Delete the edge and its twin be rearranging pointers
HalfEdge<P> xy = this;
HalfEdge<P> yx = getTwin();
HalfEdge<P> yz = xy.getNext();
HalfEdge<P> wy = yx.getPrevious();
wy.setNext(yz);
HalfEdge<P> ux = xy.getPrevious();
HalfEdge<P> xt = yx.getNext();
ux.setNext(xt);
// 3. update the edge of the survived face since it might be the twin.
getTwin().getFace().setEdge(ux);
} // the edge is on the border, therefore we remove the whole non-border face if this face does only consist of <= 3 edges before the deletion.
else {
Face<P> borderFace = getFace().isBorder() ? getFace() : getTwin().getFace();
Face<P> nonBorderFace = getFace().isBorder() ? getTwin().getFace() : getFace();
HalfEdge<P> borderHe = getFace().isBorder() ? this : getTwin();
HalfEdge<P> nonBorderHe = getFace().isBorder() ? getTwin() : this;
// nonBorder-Face is not a triangle
if(!nonBorderHe.getNext().getTwin().getFace().isBorder() && !nonBorderHe.getPrevious().getTwin().getFace().isBorder()) {
for(HalfEdge<P> halfEdge : nonBorderFace) {
halfEdge.setFace(borderFace);
}
// since the face may has this edge as pointer which will be invalid
borderFace.setEdge(borderHe.getNext());
//nonBorderFace.setEdge(nonBorderHe.getNext());
nonBorderHe.getPrevious().setNext(borderHe.getNext());
nonBorderHe.getNext().setPrevious(borderHe.getPrevious());
}
// special case1: there is no possibility to delete this edge without deleting the vertex, since the vertex has degree 2.
else if(!borderHe.equals(borderHe.getNext().getNext().getNext())) {
borderFace.setEdge(borderHe.getNext());
nonBorderFace.setEdge(nonBorderHe.getNext());
borderHe.getPrevious().setNext(borderHe.getNext());
nonBorderHe.getPrevious().setNext(nonBorderHe.getNext());
deleteVertex = true;
}
// special case2: inner face and outer face is a triangle => there is only 1 inner face.
// if we delete the edge there is no face in the geometry. Therefore we delete the whole triangle.
else {
HalfEdge<P> y = getNext();
HalfEdge<P> z = y.getNext();
// delete pointers for the GC
y.getTwin().destroy();
y.destroy();
z.getTwin().destroy();
z.destroy();
deleteVertex = true;
}
}
// delete pointers for the GC
getTwin().destroy();
destroy();
return deleteVertex;
}
/**
* removes the cyclic pointer structure such that the GC can delete these objects.
*/
private void destroy() {
setNext(null);
setPrevious(null);
setTwin(null);
setFace(null);
}
public boolean isValid() {
return twin != null && next != null && previous != null && face != null;
}
public void setTwin(final HalfEdge twin) {
this.twin = twin;
if(twin.getTwin() != this) {
if(twin != null && twin.getTwin() != this) {
twin.setTwin(this);
}
}
public void setPrevious(final @NotNull HalfEdge<P> previous) {
public void setPrevious(final HalfEdge<P> previous) {
this.previous = previous;
if(previous.getNext() != this) {
if(previous != null && previous.getNext() != this) {
previous.setNext(this);
}
}
public void setNext(final @NotNull HalfEdge<P> next) {
public void setNext(final HalfEdge<P> next) {
this.next = next;
if(next.getPrevious() != this) {
if(next != null && next.getPrevious() != this) {
next.setPrevious(this);
}
}
......@@ -101,19 +211,34 @@ public class HalfEdge<P extends IPoint> {
return new VLine((VPoint) this.getPrevious().getEnd(), (VPoint) this.getEnd());
}
public double getData() {
return data;
public Iterator<HalfEdge<P>> incidentVertexIterator() {
return new NeighbourIterator();
}
public void setData(double data) {
this.data = data;
public Iterator<Face<P>> incidentFaceIterator() { return new NeighbourFaceIterator(); }
public List<HalfEdge<P>> getIncidentPoints() {
List<HalfEdge<P>> incidentPoints = new ArrayList<>();
Iterator<HalfEdge<P>> iterator = incidentVertexIterator();
while (iterator.hasNext()) {
incidentPoints.add(iterator.next());
}
return incidentPoints;
}
public Iterator<HalfEdge<P>> incidentPointIterator() {
return new NeighbourIterator();
@Override
public String toString() {
return getEnd().toString();
}
public Iterator<Face<P>> inciedentFaceIterator() { return new NeighbourFaceIterator(); }
@Override
public Iterator<HalfEdge<P>> iterator() {
return incidentVertexIterator();
}
/**
* This iterator assumes that the this edge is completely surrounded by faces.
......@@ -156,8 +281,31 @@ public class HalfEdge<P extends IPoint> {
public HalfEdge<P> next() {
HalfEdge<P> result = current;
current = result.getTwin().getNext();
first = false;
return result;
}
}
/*
* A half-edge is defined by its end vertex and its face. In a geometry there can not be more than
* one half-edge part of face and ending at end.
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HalfEdge<?> halfEdge = (HalfEdge<?>) o;
if (!end.equals(halfEdge.end)) return false;
return face != null ? face.equals(halfEdge.face) : halfEdge.face == null;
}
@Override
public int hashCode() {
int result = end.hashCode();
result = 31 * result + (face != null ? face.hashCode() : 0);
return result;
}
}
......@@ -11,6 +11,6 @@ public interface Triangulation<P extends IPoint> {
Face<P> locate(final IPoint point);
Stream<Face<P>> streamFaces();
Set<Face<P>> getFaces();
void insert(final P point);
HalfEdge<P> insert(final P point);
void remove(final P point);
}
......@@ -18,20 +18,54 @@ public class InterpolationUtil {
public static double barycentricInterpolation(final Face<PotentialPoint> triangle, final double x, final double y){
List<PotentialPoint> points = triangle.getPoints();
if(points.size() != 3) {
System.out.println("error");
}
assert points.size() == 3;
PotentialPoint p1 = points.get(0);
PotentialPoint p2 = points.get(1);
PotentialPoint p3 = points.get(2);
VTriangle vtriangle = triangle.toTriangle();
double totalArea = vtriangle.getArea();
assert totalArea > 0;
VPoint point = new VPoint(x, y);
double percentP1 = totalArea / (new VTriangle(new VPoint(p2), new VPoint(p3), point).getArea());
double percentP2 = totalArea / (new VTriangle(new VPoint(p1), new VPoint(p3), point).getArea());
double percentP3 = totalArea / (new VTriangle(new VPoint(p1), new VPoint(p2), point).getArea());
double value = 0.0;
if(point.equals(p1)) {
value = p1.getPotential();
}
else if(point.equals(p2)) {
value = p2.getPotential();
}
else if(point.equals(p3)) {
value = p3.getPotential();
}
else {
double area1 = new VTriangle(new VPoint(p2), new VPoint(p3), point).getArea();
double area2 = new VTriangle(new VPoint(p1), new VPoint(p3), point).getArea();
double area3 = new VTriangle(new VPoint(p1), new VPoint(p2), point).getArea();
if(area1 > 0.0) {
double percentP1 = area1 / totalArea;
value += percentP1 * p1.getPotential();
}
if(area2 > 0.0) {
double percentP2 = area2 / totalArea;
value += percentP2 * p2.getPotential();
}
double value = percentP1 * p1.getPotential() + percentP2 * p2.getPotential() + percentP3 * p3.getPotential();
if(area3 > 0.0) {
double percentP3 = area3 / totalArea;
value += percentP3 * p3.getPotential();
}
}