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

implement a first simple point location algorithm

parent 3b40bde8
......@@ -2,10 +2,14 @@ package org.vadere.util.delaunay;
import org.jetbrains.annotations.NotNull;
import org.vadere.util.geometry.shapes.VPoint;
import org.vadere.util.geometry.shapes.VPolygon;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class Face implements Iterable<HalfEdge> {
......@@ -14,7 +18,13 @@ public class Face implements Iterable<HalfEdge> {
*/
private HalfEdge edge;
public Face(final @NotNull HalfEdge edge) {
public Face(final HalfEdge edge) {
this.edge = edge;
}
public Face() {}
public void setEdge(@NotNull HalfEdge edge) {
this.edge = edge;
}
......@@ -37,11 +47,28 @@ public class Face implements Iterable<HalfEdge> {
return list;
}
public boolean contains(final VPoint point) {
return toPolygon().contains(point);
}
public VPolygon toPolygon() {
Path2D path2D = new Path2D.Double();
path2D.moveTo(edge.getPrevious().getEnd().getX(), edge.getPrevious().getEnd().getY());
for(HalfEdge edge : this) {
path2D.lineTo(edge.getEnd().getX(), edge.getEnd().getY());
}
return new VPolygon(path2D);
}
@Override
public Iterator<HalfEdge> iterator() {
return new HalfEdgeIterator();
}
public Stream<HalfEdge> stream () {
Iterable<HalfEdge> iterable = () -> iterator();
return StreamSupport.stream(iterable.spliterator(), false);
}
private class HalfEdgeIterator implements Iterator<HalfEdge> {
private HalfEdge currentHalfEdge;
......
package org.vadere.util.delaunay;
import org.jetbrains.annotations.NotNull;
import org.vadere.util.geometry.shapes.VLine;
import org.vadere.util.geometry.shapes.VPoint;
/**
* Created by bzoennchen on 13.11.16.
*/
import java.util.Optional;
public class HalfEdge {
/**
......@@ -36,11 +36,15 @@ public class HalfEdge {
public HalfEdge (final VPoint end, final Face face) {
public HalfEdge (@NotNull final VPoint end, @NotNull final Face face) {
this.end = end;
this.face = face;
}
public Face getFace() {
return face;
}
public VPoint getEnd() {
return end;
}
......@@ -57,13 +61,13 @@ public class HalfEdge {
return previous;
}
public HalfEdge getTwin() {
return twin;
public Optional<HalfEdge> getTwin() {
return Optional.ofNullable(twin);
}
public void setTwin(final @NotNull HalfEdge twin) {
this.twin = twin;
if(twin.getTwin() != this) {
if(!twin.getTwin().isPresent() || twin.getTwin().get() != this) {
twin.setTwin(this);
}
}
......@@ -82,6 +86,10 @@ public class HalfEdge {
}
}
public VLine toLine() {
return new VLine(this.getPrevious().getEnd(), this.getEnd());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
......
package org.vadere.util.delaunay;
import com.sun.javafx.UnmodifiableArrayList;
import org.vadere.util.geometry.shapes.VLine;
import org.vadere.util.geometry.shapes.VPoint;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class PointLocation {
private final Collection<Face> faces;
private final List<VPoint> orderedPointList;
private final List<List<HalfEdge>> halfeEdgesSegments;
private final List<List<VPoint>> intersectionPointsInSegment;
private Comparator<VPoint> pointComparatorX = (p1, p2) -> {
double dx = p1.getX() - p2.getX();
if(dx < 0) return -1;
else if(dx > 0) return 1;
else return 0;
};
private Comparator<VPoint> pointComparatorY = (p1, p2) -> {
double dy = p1.getY() - p2.getY();
if(dy < 0) return -1;
else if(dy > 0) return 1;
else return 0;
};
private class BetweenTwoPoints implements Predicate<HalfEdge> {
private VPoint p1;
private VPoint p2;
private BetweenTwoPoints(final VPoint p1, final VPoint p2) {
this.p1 = p1;
this.p2 = p2;
}
@Override
public boolean test(final HalfEdge halfEdge) {
return (halfEdge.getEnd().getX() > p1.getX() && halfEdge.getPrevious().getEnd().getX() < p2.getX()) ||
(halfEdge.getEnd().getX() > p2.getX() && halfEdge.getPrevious().getEnd().getX() < p1.getX());
}
}
private class HalfEdgeComparator implements Comparator<HalfEdge> {
private double x1;
private double x2;
private HalfEdgeComparator(final double x1, final double x2) {
this.x1 = x1;
this.x2 = x2;
}
@Override
public int compare(final HalfEdge edge1, final HalfEdge edge2) {
VLine line1 = edge1.toLine();
VLine line2 = edge2.toLine();
double slope1 = line1.slope();
double slope2 = line2.slope();
double yIntersection1 = line1.getY1() + (x1-line1.getX1()) * slope1;
double yIntersection2 = line2.getY1() + (x1 - line2.getX1()) * slope2;
if(yIntersection1 < yIntersection2) return -1;
else if(yIntersection1 > yIntersection2) return 1;
else return 0;
}
}
public PointLocation(final Collection<Face> faces) {
this.faces = faces;
//TODO distinct is maybe slow here
Set<VPoint> pointSet = faces.stream()
.flatMap(face -> face.stream()).map(edge -> edge.getEnd())
.sorted(pointComparatorX).collect(Collectors.toSet());
orderedPointList = pointSet.stream().sorted(pointComparatorX).collect(Collectors.toList());
halfeEdgesSegments = new ArrayList<>(orderedPointList.size()-1);
intersectionPointsInSegment = new ArrayList<>(orderedPointList.size()-1);
for(int i = 0; i < orderedPointList.size() - 1; i++) {
VPoint p1 = orderedPointList.get(i);
VPoint p2 = orderedPointList.get(i+1);
List<HalfEdge> halfEdges = faces.stream().flatMap(face -> face.stream()).filter(new BetweenTwoPoints(p1, p2))
.sorted(new HalfEdgeComparator(p1.getX(), p2.getX())).collect(Collectors.toList());
List<VPoint> intersectionPoints = halfEdges.stream()
.map(hf -> hf.toLine())
.map(line -> intersectionWithX(p1.getX(), line)).collect(Collectors.toList());
halfeEdgesSegments.add(halfEdges);
intersectionPointsInSegment.add(intersectionPoints);
}
}
private static VPoint intersectionWithX(double x, VLine line) {
return new VPoint(x, (line.getY1() + (line.getX1()-x) * line.slope()));
}
public Optional<Face> getFace(final VPoint point) {
int index = Collections.binarySearch(orderedPointList, point, pointComparatorX);
int xSegmentIndex = (index >= 0) ? index : -index - 2;
if(xSegmentIndex < 0 || xSegmentIndex >= intersectionPointsInSegment.size()) {
return Optional.empty();
}
index = Collections.binarySearch(intersectionPointsInSegment.get(xSegmentIndex), point, pointComparatorY);
int ySegmentIndex = (index >= 0) ? index : -index - 2;
if(ySegmentIndex < 0 || ySegmentIndex >= halfeEdgesSegments.get(xSegmentIndex).size()) {
return Optional.empty();
}
HalfEdge edge = halfeEdgesSegments.get(xSegmentIndex).get(ySegmentIndex);
if(edge.getFace().contains(point)) {
return Optional.of(edge.getFace());
}
else {
if(edge.getTwin().isPresent()) {
return Optional.of(edge.getTwin().get().getFace());
}
else {
return Optional.empty();
}
}
}
}
......@@ -19,6 +19,10 @@ public class VLine extends Line2D.Double {
return super.ptSegDist(point.x, point.y);
}
public double slope() {
return (y2 - y1) / (x2 - x1);
}
public double distance(VPoint point) {
return GeometryUtils.closestToSegment(this, point).distance(point);
}
......
......@@ -48,6 +48,18 @@ public class VPoint implements Cloneable {
return new VPoint(x, y);
}
@Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits(x);
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(y);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
......
......@@ -20,15 +20,16 @@ public class TestFace {
@Before
public void setUp() throws Exception {
face = new Face();
HalfEdge halfEdge1 = new HalfEdge(new VPoint(0,0), face);
HalfEdge halfEdge2 = new HalfEdge(new VPoint(3,0), face);
HalfEdge halfEdge3 = new HalfEdge(new VPoint(3,5), face);
HalfEdge halfEdge3 = new HalfEdge(new VPoint(1.5,3.0), face);
halfEdge1.setNext(halfEdge2);
halfEdge2.setNext(halfEdge3);
halfEdge3.setNext(halfEdge1);
face = new Face(halfEdge1);
face.setEdge(halfEdge1);
}
@Test
......
package org.vadere.util.geometry;
import org.junit.Before;
import org.junit.Test;
import org.vadere.util.delaunay.Face;
import org.vadere.util.delaunay.HalfEdge;
import org.vadere.util.delaunay.PointLocation;
import org.vadere.util.geometry.shapes.VPoint;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
import static org.junit.Assert.assertEquals;
/**
* Created by bzoennchen on 15.11.16.
*/
public class TestSimplePointLocation {
private static Face face1;
private static Face face2;
private static double EPSILON = 1.0e-10;
@Before
public void setUp() throws Exception {
face1 = new Face();
face2 = new Face();
HalfEdge halfEdge1 = new HalfEdge(new VPoint(0,0), face1);
HalfEdge halfEdge2 = new HalfEdge(new VPoint(3,0), face1);
HalfEdge halfEdge3 = new HalfEdge(new VPoint(1.5,3.0), face1);
HalfEdge halfEdge4 = new HalfEdge(new VPoint(3.0,0), face2);
halfEdge4.setTwin(halfEdge3);
HalfEdge halfEdge5 = new HalfEdge(new VPoint(4.5,3.0), face2);
HalfEdge halfEdge6 = new HalfEdge(new VPoint(1.5,3.0), face2);
halfEdge4.setNext(halfEdge5);
halfEdge5.setNext(halfEdge6);
halfEdge6.setNext(halfEdge4);
face2.setEdge(halfEdge4);
halfEdge1.setNext(halfEdge2);
halfEdge2.setNext(halfEdge3);
halfEdge3.setNext(halfEdge1);
face1.setEdge(halfEdge1);
}
@Test
public void testFaceIterator() {
PointLocation pointLocation = new PointLocation(Arrays.asList(face1, face2));
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)));
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment