Skip to content

Commit

Permalink
front chain packer signature
Browse files Browse the repository at this point in the history
  • Loading branch information
micycle1 committed May 21, 2024
1 parent 3494e64 commit 1e61ee6
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* `boundsCenter()` to `PGS_Transformation`. Computes the center of the bounding box of a shape.
* Additional method signature for `delaunayTriangulation(points)` that supports a boundary constraint.
* `fix()` to `PGS_Processing`. Attempts to fix shapes with invalid geometry.
* Additional method signature for `frontChainPack()` that accepts a random seed.

### Changes
* Packed circles from `PGS_CirclePacking.stochasticPack()` will now always lie within a shape bounds.
Expand Down
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<version>3.12.1</version>
<configuration>
<release>17</release>
</configuration>
Expand All @@ -42,7 +42,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<version>3.6.3</version>
<configuration>
<failOnError>false</failOnError>
<subpackages>micycle.pgs</subpackages>
Expand Down Expand Up @@ -201,7 +201,7 @@
<dependency>
<groupId>com.github.micycle1</groupId>
<artifactId>processing-core-4</artifactId>
<version>4.2.1</version>
<version>4.3</version>
<scope>provided</scope> <!-- don't package into uber jar -->
<optional>true</optional> <!-- user artifact should override when provided -->
</dependency>
Expand Down
8 changes: 4 additions & 4 deletions src/main/java/micycle/pgs/PGS.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.noding.BasicSegmentString;
import org.locationtech.jts.noding.NodedSegmentString;
import org.locationtech.jts.noding.Noder;
import org.locationtech.jts.noding.SegmentString;
import org.locationtech.jts.noding.snap.SnappingNoder;
import org.locationtech.jts.noding.snapround.SnapRoundingNoder;
import org.locationtech.jts.operation.linemerge.LineMerger;
import org.locationtech.jts.operation.polygonize.Polygonizer;
import micycle.pgs.color.Colors;
Expand Down Expand Up @@ -104,7 +104,7 @@ static final LineString createLineString(PVector a, PVector b) {
}

static final SegmentString createSegmentString(PVector a, PVector b) {
return new BasicSegmentString(new Coordinate[] { PGS.coordFromPVector(a), PGS.coordFromPVector(b) }, null);
return new NodedSegmentString(new Coordinate[] { PGS.coordFromPVector(a), PGS.coordFromPVector(b) }, null);
}

static final Point createPoint(double x, double y) {
Expand Down Expand Up @@ -300,7 +300,7 @@ static final Collection<SegmentString> nodeSegmentStrings(Collection<SegmentStri
* are generally caused by nearly coincident line segments, or by very short
* line segments. Snapping mitigates both of these situations.".
*/
final Noder noder = new SnappingNoder(1e-2);
Noder noder = new SnapRoundingNoder(new PrecisionModel(-5e-3));
noder.computeNodes(segments);
return noder.getNodedSubstrings();
}
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/micycle/pgs/PGS_CirclePacking.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.tinspin.index.PointDistance;
import org.tinspin.index.PointMap;
import org.tinspin.index.covertree.CoverTree;

import micycle.pgs.commons.FrontChainPacker;
import micycle.pgs.commons.LargestEmptyCircles;
import micycle.pgs.commons.RepulsionCirclePack;
Expand Down Expand Up @@ -258,6 +259,27 @@ public static List<PVector> stochasticPack(final PShape shape, final int points,
* the center point and .z represents radius.
*/
public static List<PVector> frontChainPack(PShape shape, double radiusMin, double radiusMax) {
return frontChainPack(shape, radiusMin, radiusMax, System.nanoTime());
}

/**
* Generates a random circle packing of tangential circles with varying radii
* that overlap the given shape. The method name references the packing
* algorithm used (Front Chain Packing), rather than any particular
* characteristic of the circle packing.
* <p>
* You can set <code>radiusMin</code> equal to <code>radiusMax</code> for a
* packing of equal-sized circles using this approach.
*
* @param shape the shape within which to generate the circle packing
* @param radiusMin minimum radius of circles in the packing
* @param radiusMax maximum radius of circles in the packing
* @param seed random seed used to initialize the underlying random number
* generator
* @return A list of PVectors, each representing one circle: (.x, .y) represent
* the center point and .z represents radius.
*/
public static List<PVector> frontChainPack(PShape shape, double radiusMin, double radiusMax, long seed) {
radiusMin = Math.max(1f, Math.min(radiusMin, radiusMax)); // choose min and constrain
radiusMax = Math.max(1f, Math.max(radiusMin, radiusMax)); // choose max and constrain
final Geometry g = fromPShape(shape);
Expand Down
11 changes: 7 additions & 4 deletions src/main/java/micycle/pgs/commons/FrontChainPacker.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import processing.core.PVector;

/**
Expand All @@ -25,6 +26,7 @@ public class FrontChainPacker {
private final float width, height;
private final float offsetX, offsetY;
private final float radiusMin, radiusMax;
private final XoRoShiRo128PlusRandom rand;

/**
* The square of the max euclidean distance between a circle center (of
Expand All @@ -48,7 +50,7 @@ public class FrontChainPacker {
* @see #FrontChainPacker(float, float, float, float, float, float)
*/
public FrontChainPacker(float width, float height, float radiusMin, float radiusMax) {
this(width, height, radiusMin, radiusMax, 0, 0);
this(width, height, radiusMin, radiusMax, 0, 0, System.nanoTime());
}

/**
Expand All @@ -62,14 +64,15 @@ public FrontChainPacker(float width, float height, float radiusMin, float radius
* @param radiusMax maximum radius of circles in the packing
* @see #FrontChainPacker(float, float, float, float)
*/
public FrontChainPacker(float width, float height, float radiusMin, float radiusMax, float offsetX, float offsetY) {
public FrontChainPacker(float width, float height, float radiusMin, float radiusMax, float offsetX, float offsetY, long seed) {
this.width = width;
this.height = height;
this.radiusMin = Math.max(1f, Math.min(radiusMin, radiusMax)); // choose min and constrain
this.radiusMax = Math.max(1f, Math.max(radiusMin, radiusMax)); // choose max and constrain
this.offsetX = offsetX;
this.offsetY = offsetY;
this.maxDistSq = this.width * this.height / 2 + this.radiusMax * this.radiusMax;
rand = new XoRoShiRo128PlusRandom(seed);

this.circles = pack(new ArrayList<>());
}
Expand Down Expand Up @@ -234,7 +237,7 @@ private float score(Node node) {
}

private float randomRadius() {
return radiusMin == radiusMax ? radiusMin : (float) ThreadLocalRandom.current().nextDouble(radiusMin, radiusMax);
return radiusMin == radiusMax ? radiusMin : rand.nextFloat(radiusMin, radiusMax);
}

private static class Node {
Expand Down

0 comments on commit 1e61ee6

Please sign in to comment.