Skip to content

Commit

Permalink
Add Delaunay Triangulation pointset method signatures
Browse files Browse the repository at this point in the history
Add mosiac example
Few small fixes
  • Loading branch information
micycle1 committed Jun 13, 2021
1 parent 2089222 commit 8e65a47
Show file tree
Hide file tree
Showing 14 changed files with 158 additions and 25 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ All notable changes to PGS will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Dates are *YYYY-MM-DD*.

## [1.1.0] – 2021-06-xx
## [1.1.0] – 2021-06-13

### Added

Expand All @@ -16,8 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `createHeart()` to `PGS_Construction`. Generates heart-shaped PShapes.
- `urquhartFaces()` to `PGS_Triangulation`. Tesselates a triangulation into polygons corresponding to the faces of an _Urquhart graph_.
- `gabrielFaces()` to `PGS_Triangulation`. Tesselates a triangulation into polygons corresponding to the faces of an _Gabriel graph_.
- A new `earCutTriangulation()` method signature that takes in a PShape argument (previously it accepted a list of points only)
- Additional method signature for`earCutTriangulation()` accepts a PShape argument (previously it accepted a list of points only)
- Additional method signature for `generateRandomPoints()` that accepts a random seed.
- Additional method signature for each of the existing 3 *Delaunay Triangulation* methods, accepting a collection of points only.
- Expand `PGS_Conversion` to support conversion between:
- `PATH` PShape<->JTS `LineString`
- `POINTS` PShape<->JTS `MultiPoint`
Expand All @@ -29,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Compute (rather than ignore) circle sites of radius 0 (these are effectively points) in `PGS_Voronoi.voronoiCirclesDiagram()`
- Replaced the algorithm used by `PGS_Processing.generateRandomPoints()` with a triangulation-based approach. The new approach is ~10x faster!
- Renamed `delaunayTriangulationTin()` to `delaunayTriangulationMesh()`.
- Renamed `poissonTriangulation()` to `poissonTriangulationPoints()` (the method of the same original name now outputs a PShape).

### Fixed
- Error when `concaveHull2()` was called with alpha > 1.
Expand Down
4 changes: 4 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ All examples are dynamic and/or interactive. Shown below are merely screenshots.

<img src="../resources/examples/minkShearLetters.png" alt="" width="500"/>

## mosaic

<img src="../resources/examples/mosaic.png" alt="" width="500"/>

## partitionSmooth

<img src="../resources/examples/partitionSmooth.png" alt="" width="500"/>
Expand Down
66 changes: 66 additions & 0 deletions examples/mosaic/mosaic.pde
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import micycle.pgs.*;
import micycle.pgs.utility.PoissonDistribution;
import java.util.List;
import org.tinfour.standard.IncrementalTin;

ArrayList<PShape> shapes;

void setup() {
size(800, 800, FX2D);
smooth();
colorMode(HSB, 1, 1, 1);
run();
}

void draw() {
background(0, 0, 1);

for (PShape shape : shapes) {
shape(shape);
}

if (frameCount % 90 == 0) {
run();
}
}

void run() {
shapes = new ArrayList<PShape>();
shapes.add(prepareFaces(randomPoints(round(random(150, 350)), 5, 0, width/2, 0, height/2)));
shapes.add(prepareFaces(randomPoints(round(random(150, 350)), 5, width/2, width, 0, height/2)));
shapes.add(prepareFaces(randomPoints(round(random(150, 350)), 5, 0, width/2, height/2, height)));
shapes.add(prepareFaces(randomPoints(round(random(150, 350)), 5, width/2, width, height/2, height)));
}

ArrayList<PVector> randomPoints(int n, int buffer, float xMin, float xMax, float yMin, float yMax) {
ArrayList<PVector> randomPoints = new ArrayList<PVector>();
for (int i = 0; i < n; i++) {
randomPoints.add(new PVector(random(xMin + buffer, xMax - buffer), random(yMin+buffer, yMax - buffer)));
}
return randomPoints;
}

PShape prepareFaces(ArrayList<PVector> points) {
PShape hull = PGS_Processing.concaveHull2(points, random(0.15, 0.3));
IncrementalTin mesh = PGS_Triangulation.delaunayTriangulationMesh(hull, points, true, random(1) > 0.5 ? 0 : random(1) > 0.25 ? 1 : 2, true);

PShape faces;
if (random(1) > 0.2) {
faces = PGS_Triangulation.urquhartFaces(mesh, true);
} else {
faces = PGS_Triangulation.gabrielFaces(mesh);
}


float hueOffset = random(1);
boolean outline = random(1) > 0.33;
for (int i = 0; i < faces.getChildCount(); i++) {
PShape face = faces.getChild(i);
PVector centroid = PGS_ShapePredicates.centroid(face);
int fill = color((noise(centroid.x*0.1, centroid.y*0.1)+hueOffset)%1, random(0.75, 1), random(0.9, 1));
face.setStroke(outline ? 0 : fill);
face.setFill(fill);
face.setStrokeWeight(2);
}
return faces;
}
2 changes: 1 addition & 1 deletion examples/partitionSmooth/partitionSmooth.pde
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ void setup() {
smooth();

List<PVector> randomPoints = new PoissonDistribution().generate(30, 30, width - 30, height - 30, 35, 7);
polygon = PGS_Processing.concaveHull(randomPoints, 25);
polygon = PGS_Processing.concaveHullBFS(randomPoints, 25);

List<PShape> partitions = PGS_Processing.partition(polygon);
subPartitions = new ArrayList<PShape>();
Expand Down
2 changes: 1 addition & 1 deletion examples/triangulate/triangulate.pde
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ void draw() {
polygon = PGS_Construction.createRandomPolygonExact((int) random(3, 7), width, height);
}

List<PVector> trianglePoints = PGS_Triangulation.poissonTriangulation(polygon, 40);
List<PVector> trianglePoints = PGS_Triangulation.poissonTriangulationPoints(polygon, 40);

beginShape(TRIANGLES);
strokeWeight(2);
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>micycle</groupId>
<artifactId>PGS</artifactId>
<version>1.1.0-SNAPSHOT</version>
<version>1.1.0</version>
<name>Processing Geometry Suite</name>
<description>Geometric algorithms for Processing</description>

Expand Down
Binary file added resources/examples/mosaic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/main/java/micycle/pgs/PGS_CirclePacking.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ public static List<PVector> stochasticPack(final PShape shape, final int points,
List<PVector> steinerPoints = PGS_Processing.generateRandomPoints(shape, points);
if (triangulatePoints) {
final IncrementalTin tin = PGS_Triangulation.delaunayTriangulationMesh(shape, steinerPoints, true, 1, true);
steinerPoints = StreamSupport.stream(tin.triangles().spliterator(), false).filter(filterBorderTriangles).map(t -> centroid(t))
.collect(Collectors.toList());
steinerPoints = StreamSupport.stream(tin.triangles().spliterator(), false).filter(filterBorderTriangles)
.map(PGS_CirclePacking::centroid).collect(Collectors.toList());
}

// Model shape vertices as circles of radius 0, to constrain packed circles
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/micycle/pgs/PGS_Construction.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import micycle.pgs.color.RGB;
import micycle.pgs.utility.RandomPolygon;
import micycle.pgs.utility.Star;
import processing.core.PConstants;
import processing.core.PShape;

/**
Expand Down Expand Up @@ -193,7 +194,7 @@ public static PShape createHeart(final double x, final double y, final double wi
angle += angleInc;
}

heart.endShape();
heart.endShape(PConstants.CLOSE);
return heart;
}

Expand Down
1 change: 0 additions & 1 deletion src/main/java/micycle/pgs/PGS_Conversion.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ public static PShape toPShape(final Geometry g) {
* Converts a collection of JTS Geometries to an equivalent GROUP PShape.
*/
public static PShape toPShape(Collection<Geometry> geometries) {
System.out.println(geometries.size());
PShape shape = new PShape(GROUP);
shape.setFill(true);
shape.setFill(micycle.pgs.color.RGB.WHITE);
Expand Down
24 changes: 20 additions & 4 deletions src/main/java/micycle/pgs/PGS_Processing.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,13 @@ public static PShape densify(PShape shape, double distanceTolerance) {
*/
public static PVector pointOnExterior(PShape shape, double distance, double offsetDistance) {
distance %= 1;
// NOTE cast to polygon
LengthIndexedLine l = new LengthIndexedLine(((Polygon) fromPShape(shape)).getExteriorRing());

Geometry g = fromPShape(shape);
if (!g.getGeometryType().equals(Geometry.TYPENAME_LINEARRING) && !g.getGeometryType().equals(Geometry.TYPENAME_LINESTRING)) {
g = ((Polygon) g).getExteriorRing();
}
LengthIndexedLine l = new LengthIndexedLine(g);

Coordinate coord = l.extractPoint(distance * l.getEndIndex(), offsetDistance);
return new PVector((float) coord.x, (float) coord.y);
}
Expand All @@ -123,7 +128,13 @@ public static List<PVector> pointsOnExterior(PShape shape, int points, double of
// TODO another method that returns concave hull of returned points (when
// offset)
ArrayList<PVector> coords = new ArrayList<>(points);
LengthIndexedLine l = new LengthIndexedLine(((Polygon) fromPShape(shape)).getExteriorRing());

Geometry g = fromPShape(shape);
if (!g.getGeometryType().equals(Geometry.TYPENAME_LINEARRING) && !g.getGeometryType().equals(Geometry.TYPENAME_LINESTRING)) {
g = ((Polygon) g).getExteriorRing();
}
LengthIndexedLine l = new LengthIndexedLine(g);

final double increment = 1d / points;
for (double distance = 0; distance < 1; distance += increment) {
Coordinate coord = l.extractPoint(distance * l.getEndIndex(), offsetDistance);
Expand All @@ -143,7 +154,12 @@ public static List<PVector> pointsOnExterior(PShape shape, int points, double of
*/
public static List<PVector> pointsOnExterior(PShape shape, double interPointDistance, double offsetDistance) {
// TODO points on holes
LengthIndexedLine l = new LengthIndexedLine(((Polygon) fromPShape(shape)).getExteriorRing());
Geometry g = fromPShape(shape);
if (!g.getGeometryType().equals(Geometry.TYPENAME_LINEARRING) && !g.getGeometryType().equals(Geometry.TYPENAME_LINESTRING)) {
g = ((Polygon) g).getExteriorRing();
}
LengthIndexedLine l = new LengthIndexedLine(g);

if (interPointDistance > l.getEndIndex()) {
System.err.println("Interpoint length greater than shape length");
return new ArrayList<>();
Expand Down
64 changes: 55 additions & 9 deletions src/main/java/micycle/pgs/PGS_Triangulation.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import micycle.pgs.PGS.LinearRingIterator;
import micycle.pgs.color.RGB;
import micycle.pgs.utility.PoissonDistribution;
import processing.core.PConstants;
import processing.core.PShape;
import processing.core.PVector;

Expand Down Expand Up @@ -75,7 +76,7 @@ private PGS_Triangulation() {
* @see #delaunayTriangulationPoints(PShape, List, boolean, int, boolean)
* @see #delaunayTriangulationMesh(PShape, List, boolean, int, boolean)
*/
public static PShape delaunayTriangulation(PShape shape, List<PVector> steinerPoints, boolean constrain, int refinements,
public static PShape delaunayTriangulation(PShape shape, Collection<PVector> steinerPoints, boolean constrain, int refinements,
boolean pretty) {
final IncrementalTin tin = delaunayTriangulationMesh(shape, steinerPoints, constrain, refinements, pretty);

Expand All @@ -100,6 +101,18 @@ public static PShape delaunayTriangulation(PShape shape, List<PVector> steinerPo
return triangulation;
}

/**
* Generates a Delaunay Triangulation from a collection of points.
*
* @param points the point collection to triangulate
* @return a TRIANGLES PShape
* @see #delaunayTriangulation(PShape, Collection, boolean, int, boolean)
* @since 1.1.0
*/
public static PShape delaunayTriangulation(Collection<PVector> points) {
return delaunayTriangulation(null, points, false, 0, false);
}

/**
* Generates a Delaunay Triangulation from the given shape. The triangulation
* can be both constrained (meaning the triangulation is masked by the original
Expand Down Expand Up @@ -134,8 +147,8 @@ public static PShape delaunayTriangulation(PShape shape, List<PVector> steinerPo
* @see #delaunayTriangulationPoints(PShape, List, boolean, int, boolean)
* @see #delaunayTriangulationMesh(PShape, List, boolean, int, boolean)
*/
public static List<PVector> delaunayTriangulationPoints(PShape shape, List<PVector> steinerPoints, boolean constrain, int refinements,
boolean pretty) {
public static List<PVector> delaunayTriangulationPoints(PShape shape, Collection<PVector> steinerPoints, boolean constrain,
int refinements, boolean pretty) {
final IncrementalTin tin = delaunayTriangulationMesh(shape, steinerPoints, constrain, refinements, pretty);

final ArrayList<PVector> triangles = new ArrayList<>();
Expand All @@ -152,6 +165,22 @@ public static List<PVector> delaunayTriangulationPoints(PShape shape, List<PVect
return triangles;
}

/**
* Generates a Delaunay Triangulation from a collection of points.
* <p>
* This method returns the triangulation as a list of points, rather than a
* PShape.
*
* @param points the point collection to triangulate
* @return List of PVector coordinates, where each consecutive triplet of
* coordinates are the 3 vertices belonging to one triangle
* @see #delaunayTriangulationPoints(PShape, Collection, boolean, int, boolean)
* @since 1.1.0
*/
public static List<PVector> delaunayTriangulationPoints(Collection<PVector> points) {
return delaunayTriangulationPoints(null, points, false, 0, false);
}

/**
* Generates a Delaunay Triangulation from the given shape. The triangulation
* can be both constrained (meaning the triangulation is masked by the original
Expand Down Expand Up @@ -187,7 +216,7 @@ public static List<PVector> delaunayTriangulationPoints(PShape shape, List<PVect
*/
public static IncrementalTin delaunayTriangulationMesh(PShape shape, Collection<PVector> steinerPoints, boolean constrain,
int refinements, boolean pretty) {
final Geometry g = fromPShape(shape);
final Geometry g = shape == null ? PGS.GEOM_FACTORY.createEmpty(2) : fromPShape(shape);
final IncrementalTin tin = new IncrementalTin(10);

final ArrayList<Vertex> vertices = new ArrayList<>();
Expand Down Expand Up @@ -255,6 +284,21 @@ public static IncrementalTin delaunayTriangulationMesh(PShape shape, Collection<
return tin;
}

/**
* Generates a Delaunay Triangulation from a collection of points.
* <p>
* This method returns the triangulation in its raw form: a Triangulated
* Irregular Network (mesh).
*
* @param points the point collection to triangulate
* @return Triangulated Irregular Network object (mesh)
* @see #delaunayTriangulationMesh(PShape, Collection, boolean, int, boolean)
* @since 1.1.0
*/
public static IncrementalTin delaunayTriangulationMesh(Collection<PVector> points) {
return delaunayTriangulationMesh(null, points, false, 0, false);
}

/**
* Creates a Delaunay triangulation of the shape where additional steiner
* points, populated by poisson sampling, are included.
Expand Down Expand Up @@ -409,6 +453,7 @@ public static PShape urquhartFaces(final IncrementalTin triangulation, final boo
* 3) Merge the triangles in each group into a polygon.
*/

final boolean notConstrained = triangulation.getConstraints().size() == 0;
/*
* Build a map of edges->triangles. In combination with use getDual(), this is
* used to find a triangle edge's neighbouring triangle.
Expand All @@ -418,7 +463,7 @@ public static PShape urquhartFaces(final IncrementalTin triangulation, final boo

TriangleCollector.visitSimpleTriangles(triangulation, t -> {
final IConstraint constraint = t.getContainingRegion();
if (constraint != null && constraint.definesConstrainedRegion()) {
if (notConstrained || (constraint != null && constraint.definesConstrainedRegion())) {
map.put(t.getEdgeA(), t);
map.put(t.getEdgeB(), t);
map.put(t.getEdgeC(), t);
Expand Down Expand Up @@ -466,14 +511,15 @@ public static PShape urquhartFaces(final IncrementalTin triangulation, final boo
* @see #urquhartFaces(IncrementalTin, boolean)
*/
public static PShape gabrielFaces(final IncrementalTin triangulation) {

final boolean notConstrained = triangulation.getConstraints().size() == 0;
final HashMap<IQuadEdge, SimpleTriangle> map = new HashMap<>();
final HashSet<IQuadEdge> nonGabrielEdges = new HashSet<IQuadEdge>(); // edges to collapse
final HashSet<IQuadEdge> edges = new HashSet<IQuadEdge>();
final HashSet<Vertex> vertices = new HashSet<Vertex>(); // constrained vertices

TriangleCollector.visitSimpleTriangles(triangulation, t -> {
final IConstraint constraint = t.getContainingRegion();
if (constraint != null && constraint.definesConstrainedRegion()) {
if (notConstrained || (constraint != null && constraint.definesConstrainedRegion())) {
map.put(t.getEdgeA(), t);
map.put(t.getEdgeB(), t);
map.put(t.getEdgeC(), t);
Expand Down Expand Up @@ -616,11 +662,11 @@ private static PShape triangleToPShape(final SimpleTriangle t) {
triangle.setStrokeWeight(3);
triangle.setFill(true);
triangle.setFill(255);
triangle.beginShape(TRIANGLES);
triangle.beginShape();
triangle.vertex((float) t.getVertexA().x, (float) t.getVertexA().y);
triangle.vertex((float) t.getVertexB().x, (float) t.getVertexB().y);
triangle.vertex((float) t.getVertexC().x, (float) t.getVertexC().y);
triangle.endShape();
triangle.endShape(PConstants.CLOSE);
return triangle;
}

Expand Down
2 changes: 0 additions & 2 deletions src/main/java/micycle/pgs/PGS_Voronoi.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
import org.tinfour.voronoi.ThiessenPolygon;
import org.tinspin.index.PointDistanceFunction;
import org.tinspin.index.PointEntryDist;
import org.tinspin.index.covertree.CoverTree;
import org.tinspin.index.kdtree.KDEntryDist;
import org.tinspin.index.kdtree.KDTree;
import org.tinspin.index.rtree.Entry;
import org.tinspin.index.rtree.RTree;
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/micycle/pgs/utility/Star.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.ArrayList;
import java.util.List;

import processing.core.PConstants;
import processing.core.PShape;
import processing.core.PVector;

Expand Down Expand Up @@ -167,7 +168,7 @@ private static PShape createStarShape(double centerX, double centerY, double inn
path2.vertex(nextCenter.x, nextCenter.y);
}
}
path2.endShape();
path2.endShape(PConstants.CLOSE);

return path2;
}
Expand Down

0 comments on commit 8e65a47

Please sign in to comment.