Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add polygon triangulation algorithms and Tri data structure #775

Merged
merged 57 commits into from
Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
c4567c0
Initial commit
dr-jts Jul 22, 2021
c665806
Naming changes
dr-jts Jul 22, 2021
fcaa795
Refactor Triangle methods
dr-jts Jul 23, 2021
81d6137
Refactoring
dr-jts Jul 23, 2021
716e0f0
Refactoring
dr-jts Jul 23, 2021
36f9fdc
Refactor, clean up code
dr-jts Jul 23, 2021
fb830fc
Renaming, refactoring
dr-jts Jul 23, 2021
7a7d077
Refactor
dr-jts Jul 23, 2021
efa2b41
Refactoring
dr-jts Jul 23, 2021
e478e53
Refactoring
dr-jts Jul 23, 2021
be42f58
Refactor out ConstrainedDelaunayTriangulator
dr-jts Jul 23, 2021
63b8265
Add PolygonTriangulatorTest
dr-jts Jul 23, 2021
15f5964
Javadoc
dr-jts Jul 23, 2021
0052b9d
Add Corner envelope test
dr-jts Jul 23, 2021
9650c73
Remove debug code
dr-jts Jul 26, 2021
e613ed7
Javadoc
dr-jts Jul 26, 2021
fb088a3
Add index to PolygonEarClipper
dr-jts Jul 27, 2021
59822e7
Javadoc
dr-jts Jul 27, 2021
21f8978
Various updates
dr-jts Jul 28, 2021
5f8747d
Rename VertexSequencePackedRtree
dr-jts Jul 29, 2021
08a0943
Fix imports
dr-jts Jul 30, 2021
12d101d
Improve ApproximateMedialAxis quality
dr-jts Aug 1, 2021
4e23b83
Improve position of corridor exit points
dr-jts Aug 10, 2021
4d61afb
Improve construction of wedge exit points
dr-jts Aug 10, 2021
661da83
Improve axis
dr-jts Aug 10, 2021
a10def6
Refactoring
dr-jts Aug 12, 2021
e81e181
Cleanup, add flag for skipping collinear vertices
dr-jts Aug 25, 2021
05a63da
Javadoc
dr-jts Aug 25, 2021
62d5b48
Javadoc
dr-jts Aug 25, 2021
5771e34
Renaming, Javadoc
dr-jts Aug 26, 2021
a897c58
Imports
dr-jts Aug 26, 2021
6be44e6
Renaming
dr-jts Aug 26, 2021
57d3e6c
Fix medial axis to handle holes
dr-jts Aug 26, 2021
cae51ca
Remove bad import
dr-jts Aug 26, 2021
ec9055d
Refactoring
dr-jts Aug 27, 2021
991d933
Renaming
dr-jts Aug 27, 2021
27e6096
Clean up HoleJoiner
dr-jts Aug 28, 2021
5a8fbf2
Optimize PolygonHoleJoiner
dr-jts Aug 29, 2021
b26a272
Switch flip for swap
dr-jts Aug 30, 2021
908e259
Debugging code
dr-jts Aug 31, 2021
426d89a
Fix EarClipper to handled repeated points
dr-jts Sep 3, 2021
c955567
Javadoc
dr-jts Sep 8, 2021
e7cd95e
Clean up VSPRtree
dr-jts Sep 9, 2021
77436d6
Remove medial axis code for now
dr-jts Sep 12, 2021
7657b77
Remove medial axis test for now
dr-jts Sep 12, 2021
1c1e2bd
Refactor corner handling
dr-jts Sep 12, 2021
b3e0cbc
renaming
dr-jts Sep 12, 2021
8940086
Improve ear-clipping code
dr-jts Sep 13, 2021
d2de4e2
Refactor packages
dr-jts Sep 13, 2021
671c4d7
Improve tri.flip method to ensure adjacency
dr-jts Sep 14, 2021
0b313ab
Add TriTest
dr-jts Sep 14, 2021
e39031e
Javadoc for Tri
dr-jts Sep 14, 2021
4f61778
Javadoc
dr-jts Sep 14, 2021
8f34c43
Remove unused field
dr-jts Sep 14, 2021
7a9cf57
Fix imports
dr-jts Sep 14, 2021
1bbd505
renaming, Javadoc
dr-jts Sep 14, 2021
f7f901b
Javadoc
dr-jts Sep 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.locationtech.jts.index.strtree.Boundable;
import org.locationtech.jts.index.strtree.GeometryItemDistance;
import org.locationtech.jts.index.strtree.STRtree;
//import org.locationtech.jts.triangulatepoly.VertexSequencePackedRtree;


public class SpatialIndexFunctions
Expand Down Expand Up @@ -249,4 +250,20 @@ public static Geometry monotoneChains(Geometry geom) {
}
return geom.getFactory().buildGeometry(lines);
}

/*
public static Geometry sprTreeBounds(Geometry geom)
{
Coordinate[] pts = geom.getCoordinates();
VertexSequencePackedRtree index = new VertexSequencePackedRtree(pts);
Envelope[] bounds = index.getBounds();
Geometry[] polys = new Geometry[bounds.length];
int i = 0;
for (Envelope env : bounds) {
polys[i++] = geom.getFactory().toGeometry(env);
}
return geom.getFactory().createGeometryCollection(polys);
}
*/

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (c) 2021 Martin Davis.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jtstest.function;

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.triangulate.polygon.ConstrainedDelaunayTriangulator;
import org.locationtech.jts.triangulate.polygon.PolygonTriangulator;


public class TriangulatePolyFunctions
{
public static Geometry triangulate(Geometry geom)
{
return PolygonTriangulator.triangulate(geom);
}

public static Geometry constrainedDelaunay(Geometry geom)
{
return ConstrainedDelaunayTriangulator.triangulate(geom);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import org.locationtech.jtstest.function.SpatialIndexFunctions;
import org.locationtech.jtstest.function.SpatialPredicateFunctions;
import org.locationtech.jtstest.function.TriangleFunctions;
import org.locationtech.jtstest.function.TriangulatePolyFunctions;
import org.locationtech.jtstest.function.TriangulationFunctions;
import org.locationtech.jtstest.function.ValidationFunctions;
import org.locationtech.jtstest.function.WriterFunctions;
Expand Down Expand Up @@ -131,6 +132,7 @@ public static GeometryFunctionRegistry createTestBuilderRegistry()
funcRegistry.add(SnappingFunctions.class);
funcRegistry.add(SortingFunctions.class);
funcRegistry.add(TriangulationFunctions.class);
funcRegistry.add(TriangulatePolyFunctions.class);
funcRegistry.add(TriangleFunctions.class);
funcRegistry.add(ValidationFunctions.class);
funcRegistry.add(WriterFunctions.class);
Expand Down
68 changes: 53 additions & 15 deletions modules/core/src/main/java/org/locationtech/jts/geom/Triangle.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,9 @@ public class Triangle
* Note: this implementation is not robust for angles very close to 90
* degrees.
*
* @param a
* a vertex of the triangle
* @param b
* a vertex of the triangle
* @param c
* a vertex of the triangle
* @param a a vertex of the triangle
* @param b a vertex of the triangle
* @param c a vertex of the triangle
* @return true if the triangle is acute
*/
public static boolean isAcute(Coordinate a, Coordinate b, Coordinate c)
Expand All @@ -51,6 +48,38 @@ public static boolean isAcute(Coordinate a, Coordinate b, Coordinate c)
return false;
return true;
}

/**
* Tests whether a triangle is oriented counter-clockwise.
*
* @param a a vertex of the triangle
* @param b a vertex of the triangle
* @param c a vertex of the triangle
* @return true if the triangle orientation is counter-clockwise
*/
public static boolean isCCW(Coordinate a, Coordinate b, Coordinate c)
{
return Orientation.COUNTERCLOCKWISE == Orientation.index(a, b, c);
}

/**
* Tests whether a triangle intersects a point.
*
* @param a a vertex of the triangle
* @param b a vertex of the triangle
* @param c a vertex of the triangle
* @param p the point to test
* @return true if the triangle intersects the point
*/
public static boolean intersects(Coordinate a, Coordinate b, Coordinate c, Coordinate p)
{
int exteriorIndex = isCCW(a, b, c) ?
Orientation.CLOCKWISE : Orientation.COUNTERCLOCKWISE;
if (exteriorIndex == Orientation.index(a, b, p)) return false;
if (exteriorIndex == Orientation.index(b, c, p)) return false;
if (exteriorIndex == Orientation.index(c, a, p)) return false;
return true;
}

/**
* Computes the line which is the perpendicular bisector of the line segment
Expand Down Expand Up @@ -437,8 +466,8 @@ public static double interpolateZ(Coordinate p, Coordinate v0, Coordinate v1,
double u = (-c * dx + a * dy) / det;
double z = v0.getZ() + t * (v1.getZ() - v0.getZ()) + u * (v2.getZ() - v0.getZ());
return z;
}

}
/**
* The coordinates of the vertices of the triangle
*/
Expand Down Expand Up @@ -487,9 +516,18 @@ public Coordinate inCentre()
*/
public boolean isAcute()
{
return isAcute(this.p0, this.p1, this.p2);
return isAcute(p0, p1, p2);
}

/**
* Tests whether this triangle is oriented counter-clockwise.
*
* @return true if the triangle orientation is counter-clockwise
*/
public boolean isCCW() {
return isCCW(p0, p1, p2);
}

/**
* Computes the circumcentre of this triangle. The circumcentre is the centre
* of the circumcircle, the smallest circle which encloses the triangle. It is
Expand All @@ -507,7 +545,7 @@ public boolean isAcute()
*/
public Coordinate circumcentre()
{
return circumcentre(this.p0, this.p1, this.p2);
return circumcentre(p0, p1, p2);
}

/**
Expand All @@ -522,7 +560,7 @@ public Coordinate circumcentre()
*/
public Coordinate centroid()
{
return centroid(this.p0, this.p1, this.p2);
return centroid(p0, p1, p2);
}

/**
Expand All @@ -532,7 +570,7 @@ public Coordinate centroid()
*/
public double longestSideLength()
{
return longestSideLength(this.p0, this.p1, this.p2);
return longestSideLength(p0, p1, p2);
}

/**
Expand All @@ -545,7 +583,7 @@ public double longestSideLength()
*/
public double area()
{
return area(this.p0, this.p1, this.p2);
return area(p0, p1, p2);
}

/**
Expand All @@ -563,7 +601,7 @@ public double area()
*/
public double signedArea()
{
return signedArea(this.p0, this.p1, this.p2);
return signedArea(p0, p1, p2);
}

/**
Expand All @@ -574,7 +612,7 @@ public double signedArea()
*/
public double area3D()
{
return area3D(this.p0, this.p1, this.p2);
return area3D(p0, p1, p2);
}

/**
Expand Down
25 changes: 25 additions & 0 deletions modules/core/src/main/java/org/locationtech/jts/math/MathUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,31 @@ public static int clamp(int x, int min, int max)
return x;
}

/**
* Clamps an integer to a given maximum limit.
*
* @param x the value to clamp
* @param max the maximum value
* @return the clamped value
*/
public static int clampMax(int x, int max)
{
if (x > max) return max;
return x;
}

/**
* Computes the ceiling function of the dividend of two integers.
*
* @param num the numerator
* @param denom the denominator
* @return the ceiling of num / denom
*/
public static int ceil(int num, int denom) {
int div = num / denom;
return div * denom >= num ? div : div + 1;
}

private static final double LOG_10 = Math.log(10);

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2021 Martin Davis.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.triangulate.polygon;

import java.util.ArrayList;
import java.util.List;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.util.PolygonExtracter;
import org.locationtech.jts.triangulate.tri.Tri;
import org.locationtech.jts.triangulate.tri.TriangulationBuilder;

/**
* Computes the Constrained Delaunay Triangulation of polygons.
* The Constrained Delaunay Triangulation of a polygon is a set of triangles
* covering the polygon, with the maximum total interior angle over all
* possible triangulations. It provides the "best quality" triangulation
* of the polygon.
* <p>
* Holes are supported.
*/
public class ConstrainedDelaunayTriangulator {

/**
* Computes the Constrained Delaunay Triangulation of each polygon element in a geometry.
*
* @param geom the input geometry
* @return a GeometryCollection of the computed triangle polygons
*/
public static Geometry triangulate(Geometry geom) {
ConstrainedDelaunayTriangulator cdt = new ConstrainedDelaunayTriangulator(geom);
return cdt.compute();
}

private final GeometryFactory geomFact;
private final Geometry inputGeom;

/**
* Constructs a new Constrained Delaunay triangulator.
*
* @param inputGeom the input geometry
*/
public ConstrainedDelaunayTriangulator(Geometry inputGeom) {
geomFact = new GeometryFactory();
this.inputGeom = inputGeom;
}

private Geometry compute() {
List<Polygon> polys = PolygonExtracter.getPolygons(inputGeom);
List<Tri> triList = new ArrayList<Tri>();
for (Polygon poly : polys) {
List<Tri> polyTriList = triangulatePolygon(poly);
triList.addAll(polyTriList);
}
return Tri.toGeometry(triList, geomFact);
}

/**
* Computes the triangulation of a single polygon
* and returns it as a list of {@link Tri}s.
*
* @param poly the input polygon
* @return list of Tris forming the triangulation
*/
List<Tri> triangulatePolygon(Polygon poly) {
/**
* Normalize to ensure that shell and holes have canonical orientation.
*
* TODO: perhaps better to just correct orientation of rings?
*/
Polygon polyNorm = (Polygon) poly.norm();
Coordinate[] polyShell = PolygonHoleJoiner.join(polyNorm);
List<Tri> triList = PolygonEarClipper.triangulate(polyShell);

//long start = System.currentTimeMillis();
TriangulationBuilder.build(triList);
TriDelaunayImprover.improve(triList);
//System.out.println("swap used: " + (System.currentTimeMillis() - start) + " milliseconds");

return triList;
}

}
Loading