From 615d0094ee4ad675f9cbea8c026f374b6c8da936 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 31 Mar 2021 14:36:10 -0700 Subject: [PATCH 1/2] Fix HalfEdge.prev(), add unit tests Signed-off-by: Martin Davis --- .../locationtech/jts/edgegraph/EdgeGraph.java | 13 ++++ .../locationtech/jts/edgegraph/HalfEdge.java | 16 ++++- .../jts/edgegraph/EdgeGraphTest.java | 63 ++++++++++++++++++- 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/modules/core/src/main/java/org/locationtech/jts/edgegraph/EdgeGraph.java b/modules/core/src/main/java/org/locationtech/jts/edgegraph/EdgeGraph.java index a5649b9cc0..6c97bc7ec7 100644 --- a/modules/core/src/main/java/org/locationtech/jts/edgegraph/EdgeGraph.java +++ b/modules/core/src/main/java/org/locationtech/jts/edgegraph/EdgeGraph.java @@ -54,6 +54,13 @@ protected HalfEdge createEdge(Coordinate orig) return new HalfEdge(orig); } + /** + * Creates a HalfEge pair, using the HalfEdge type of the graph subclass. + * + * @param p0 + * @param p1 + * @return + */ private HalfEdge create(Coordinate p0, Coordinate p1) { HalfEdge e0 = createEdge(p0); @@ -136,6 +143,12 @@ private HalfEdge insert(Coordinate orig, Coordinate dest, HalfEdge eAdj) { return e; } + /** + * Gets all {@link HalfEdge}s in the graph. + * Both edges of edge pairs are included. + * + * @return a collection of the graph edges + */ public Collection getVertexEdges() { return vertexMap.values(); diff --git a/modules/core/src/main/java/org/locationtech/jts/edgegraph/HalfEdge.java b/modules/core/src/main/java/org/locationtech/jts/edgegraph/HalfEdge.java index d567a8493a..8be390d96d 100644 --- a/modules/core/src/main/java/org/locationtech/jts/edgegraph/HalfEdge.java +++ b/modules/core/src/main/java/org/locationtech/jts/edgegraph/HalfEdge.java @@ -182,17 +182,30 @@ public HalfEdge next() * Gets the previous edge CW around the origin * vertex of this edge, * with that vertex being its destination. + *

+ * It is always true that e.next().prev() == e + *

+ * Note that this requires a scan of the origin edges, + * so may not be efficient for some uses. * * @return the previous edge CW around the origin vertex */ public HalfEdge prev() { - return sym.next().sym; + HalfEdge curr = this; + HalfEdge prev = this; + do { + prev = curr; + curr = curr.oNext(); + } while (curr != this); + return prev.sym; } /** * Gets the next edge CCW around the origin of this edge, * with the same origin. * If the origin vertex has degree 1 then this is the edge itself. + *

+ * e.oNext() is equal to e.sym().next() * * @return the next edge around the origin */ @@ -467,6 +480,7 @@ public int degree() { /** * Finds the first node previous to this edge, if any. + * A node has degree <> 2. * If no such node exists (i.e. the edge is part of a ring) * then null is returned. * diff --git a/modules/core/src/test/java/org/locationtech/jts/edgegraph/EdgeGraphTest.java b/modules/core/src/test/java/org/locationtech/jts/edgegraph/EdgeGraphTest.java index 8c6c2ff853..bd035a0d6b 100644 --- a/modules/core/src/test/java/org/locationtech/jts/edgegraph/EdgeGraphTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/edgegraph/EdgeGraphTest.java @@ -12,6 +12,7 @@ package org.locationtech.jts.edgegraph; +import java.util.Collection; import java.util.List; import org.locationtech.jts.geom.Coordinate; @@ -23,11 +24,11 @@ public class EdgeGraphTest extends TestCase { + public static void main(String args[]) { TestRunner.run(EdgeGraphTest.class); } - public EdgeGraphTest(String name) { super(name); } public void testNode() throws Exception @@ -39,8 +40,40 @@ public void testNode() throws Exception }); checkNodeValid(graph, new Coordinate(0, 0), new Coordinate(1, 0)); checkEdge(graph, new Coordinate(0, 0), new Coordinate(1, 0)); + + checkNextPrev(graph); + + checkNext(graph, 1, 0, 0, 0, 0, 1); + checkNext(graph, 0, 1, 0, 0, -1, 0); + checkNext(graph, -1, 0, 0, 0, 1, 0); + + checkNextPrev(graph, 1, 0, 0, 0); + checkNextPrev(graph, 0, 1, 0, 0); + checkNextPrev(graph, -1, 0, 0, 0); + + assertTrue( findEdge(graph, 0, 0, 1, 0).degree() == 3 ); } + public void testRingGraph() throws Exception { + EdgeGraph graph = build("MULTILINESTRING ((10 10, 10 90), (10 90, 90 90), (90 90, 90 10), (90 10, 10 10))"); + HalfEdge e = findEdge(graph, 10, 10, 10, 90); + HalfEdge eNext = findEdge(graph, 10, 90, 90, 90); + assertTrue(e.next() == eNext); + assertTrue(eNext.prev() == e); + + HalfEdge eSym = findEdge(graph, 10, 90, 10, 10); + assertTrue(e.sym() == eSym); + assertTrue(e.orig().equals2D(new Coordinate(10, 10))); + assertTrue(e.dest().equals2D(new Coordinate(10, 90))); + + checkNextPrev(graph); + } + + public void testSingleEdgeGraph() throws Exception { + EdgeGraph graph = build("LINESTRING (10 10, 20 20)"); + checkNextPrev(graph); + } + /** * This test produced an error using the original buggy sorting algorithm * (in {@link HalfEdge#insert(HalfEdge)}). @@ -61,7 +94,8 @@ public void testCCWAfterInserts2() { checkNodeValid(e1); } - + //================================================== + private void checkEdgeRing(EdgeGraph graph, Coordinate p, Coordinate[] dest) { HalfEdge e = graph.findEdge(p, dest[0]); @@ -91,6 +125,31 @@ private void checkNodeValid(HalfEdge e) { assertTrue("Found non-sorted edges around node " + e, isNodeValid); } + private void checkNextPrev(EdgeGraph graph) { + Collection edges = graph.getVertexEdges(); + for (HalfEdge e: edges) { + assertTrue(e.next().prev() == e); + } + } + + + + private void checkNext(EdgeGraph graph, double x1, double y1, double x2, double y2, double x3, double y3) { + HalfEdge e1 = findEdge(graph, x1, y1, x2, y2); + HalfEdge e2 = findEdge(graph, x2, y2, x3, y3); + assertTrue(e1.next() == e2); + assertTrue(e2.prev() == e1); + } + + private void checkNextPrev(EdgeGraph graph, double x1, double y1, double x2, double y2) { + HalfEdge e = findEdge(graph, x1, y1, x2, y2); + assertTrue(e.next().prev() == e); + } + + private HalfEdge findEdge(EdgeGraph graph, double x1, double y1, double x2, double y2) { + return graph.findEdge(new Coordinate(x1, y1), new Coordinate(x2, y2)); + } + private EdgeGraph build(String wkt) throws ParseException { return build(new String[] { wkt }); } From d2729fc156f8abfa1f009b99298d9dc6a7414b09 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Wed, 31 Mar 2021 15:20:56 -0700 Subject: [PATCH 2/2] Fix OverlayGraphTest HalfEdge.prev test Signed-off-by: Martin Davis --- .../jts/operation/overlayng/OverlayGraphTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/OverlayGraphTest.java b/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/OverlayGraphTest.java index f02b0b0346..43602cb4ac 100644 --- a/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/OverlayGraphTest.java +++ b/modules/core/src/test/java/org/locationtech/jts/operation/overlayng/OverlayGraphTest.java @@ -75,9 +75,9 @@ public void testStar() { checkNext( e2, e2.symOE() ); checkNext( e3, e3.symOE() ); - checkPrev( e1, e3.symOE() ); - checkPrev( e2, e1.symOE() ); - checkPrev( e3, e2.symOE() ); + checkPrev( e1, e2.symOE() ); + checkPrev( e2, e3.symOE() ); + checkPrev( e3, e1.symOE() ); } /**