VoronoiCircularStressTest.java

/*
 * Copyright (c) 2026 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 test.jts.perf.triangulate;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.TopologyException;
import org.locationtech.jts.triangulate.VoronoiChecker;
import org.locationtech.jts.triangulate.VoronoiDiagramBuilder;

/**
 * Generates Voronoi diagrams from point sets which contain nearly cocircular sites.
 * Buffering with circular arcs is an easy way to generate nearly cocircular sites.
 * The {@link VoronoiDiagramBuilder} snapping heuristic produces 
 * valid Voronoi diagrams from this kind of input.
 * 
 * @author mdavis
 *
 */
public class VoronoiCircularStressTest {
  
  private static final int NUM_CASES = 1000;

  public static void main(String args[]) {
    VoronoiCircularStressTest test = new VoronoiCircularStressTest();
    test.run(NUM_CASES);
  }
  
  final static GeometryFactory geomFact = new GeometryFactory();
  
  private static final double WIDTH = 1000;
  
  private void run(int numCases) {
    int numValid = 0;
    for (int i = 0; i < numCases; i++) {
      boolean isValid = runVoronoi(i);
      if (isValid) numValid++;
    }
    System.out.println();
    System.out.format("Cases: %d  Valid: %d  Failures: %d\n",
        numCases, numValid, numCases - numValid);
  }

  private boolean runVoronoi(int i) {
    LineString line = createRandomLine(WIDTH, 4);
    Geometry lineBuf = line.buffer((WIDTH / 100));
    //System.out.println(lineBuf);
    boolean isValid = checkVoronoiValid(lineBuf);
    String msg = isValid ? "Valid" : ">>>>>>>  INVALID ";
    System.out.format("Case %d: %s\n", i, msg);
    return isValid;
  }

  private LineString createRandomLine(double width, int nPts) {
    Coordinate[] pts = new Coordinate[nPts];
    for (int i = 0; i < nPts; i++) {
      double x = width * Math.random();
      double y = width * Math.random();
      pts[i] = new Coordinate(x, y);
    }
    LineString line = geomFact.createLineString(pts);
    return line;
  }
  
  boolean checkVoronoiValid(Geometry sites) {
    Geometry result = null;
    try {
      result = computeVoronoi(sites); 
    }
    catch (TopologyException e) {
      return false;
    }

    return VoronoiChecker.isValid(result);
  }

  private static Geometry computeVoronoi(Geometry sites) {
    VoronoiDiagramBuilder builder = new VoronoiDiagramBuilder();
    builder.setSites(sites);
    Geometry result = builder.getDiagram(sites.getFactory());
    return result;
  }
}