Class SineStarFactory

  1 /*
  2  * Copyright (c) 2016 Vivid Solutions.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 11  */
 12  
 13 package org.locationtech.jts.geom.util;
 14  
 15 import org.locationtech.jts.geom.Coordinate;
 16 import org.locationtech.jts.geom.Envelope;
 17 import org.locationtech.jts.geom.Geometry;
 18 import org.locationtech.jts.geom.GeometryFactory;
 19 import org.locationtech.jts.geom.LinearRing;
 20 import org.locationtech.jts.geom.Polygon;
 21 import org.locationtech.jts.util.GeometricShapeFactory;
 22  
 23 /**
 24  * Creates geometries which are shaped like multi-armed stars
 25  * with each arm shaped like a sine wave.
 26  * These kinds of geometries are useful as a more complex 
 27  * geometry for testing algorithms.
 28  * 
 29  * @author Martin Davis
 30  *
 31  */
 32 public class SineStarFactory
 33     extends GeometricShapeFactory
 34 {
 35   /**
 36    * Creates a sine star with the given parameters.
 37    * 
 38    * @param origin the origin point
 39    * @param size the size of the star
 40    * @param nPts the number of points in the star
 41    * @param nArms the number of arms to generate
 42    * @param armLengthRatio the arm length ratio
 43    * @return a sine star shape
 44    */
 45   public static Geometry create(Coordinate origin, double size, int nPts, int nArms, double armLengthRatio) {
 46     SineStarFactory gsf = new SineStarFactory();
 47     gsf.setCentre(origin);
 48     gsf.setSize(size);
 49     gsf.setNumPoints(nPts);
 50     gsf.setArmLengthRatio(armLengthRatio);
 51     gsf.setNumArms(nArms);
 52     Geometry poly = gsf.createSineStar();
 53     return poly;
 54   }
 55   
 56     protected int numArms = 8;
 57     protected double armLengthRatio = 0.5;
 58     
 59   /**
 60    * Creates a factory which will create sine stars using the default
 61    * {@link GeometryFactory}.
 62    */
 63     public SineStarFactory()
 64     {
 65         super();
 66     }
 67     
 68   /**
 69    * Creates a factory which will create sine stars using the given
 70    * {@link GeometryFactory}.
 71    *
 72    * @param geomFact the factory to use
 73    */
 74   public SineStarFactory(GeometryFactory geomFact)
 75   {
 76     super(geomFact);
 77   }
 78  
 79   /**
 80    * Sets the number of arms in the star
 81    * 
 82    * @param numArms the number of arms to generate
 83    */
 84   public void setNumArms(int numArms)
 85   {
 86       this.numArms = numArms;
 87   }
 88   
 89   /**
 90    * Sets the ratio of the length of each arm to the radius of the star.
 91    * A smaller number makes the arms shorter.
 92    * Value should be between 0.0 and 1.0
 93    * 
 94    * @param armLengthRatio the ratio determining the length of them arms.
 95    */
 96   public void setArmLengthRatio(double armLengthRatio)
 97   {
 98       this.armLengthRatio = armLengthRatio;
 99   }
100   
101   /**
102    * Generates the geometry for the sine star
103    * 
104    * @return the geometry representing the sine star
105    */
106   public Geometry createSineStar()
107   {
108     Envelope env = dim.getEnvelope();
109     double radius = env.getWidth() / 2.0;
110  
111       double armRatio = armLengthRatio;
112     if (armRatio < 0.0)
113       armRatio = 0.0;
114     if (armRatio > 1.0)
115       armRatio = 1.0;
116  
117     double armMaxLen = armRatio * radius;
118     double insideRadius = (1 - armRatio) * radius;
119  
120     double centreX = env.getMinX() + radius;
121     double centreY = env.getMinY() + radius;
122  
123     Coordinate[] pts = new Coordinate[nPts + 1];
124     int iPt = 0;
125     for (int i = 0; i < nPts; i++) {
126       // the fraction of the way through the current arm - in [0,1]
127       double ptArcFrac = (i / (double) nPts) * numArms;
128       double armAngFrac = ptArcFrac - Math.floor(ptArcFrac);
129       
130       // the angle for the current arm - in [0,2Pi]  
131       // (each arm is a complete sine wave cycle)
132       double armAng = 2 * Math.PI * armAngFrac;
133       // the current length of the arm
134       double armLenFrac = (Math.cos(armAng) + 1.0) / 2.0;
135       
136       // the current radius of the curve (core + arm)
137       double curveRadius = insideRadius + armMaxLen * armLenFrac;
138  
139       // the current angle of the curve
140       double ang = i * (2 * Math.PI / nPts);
141       double x = curveRadius * Math.cos(ang) + centreX;
142       double y = curveRadius * Math.sin(ang) + centreY;
143       pts[iPt++] = coord(x, y);
144     }
145     pts[iPt] = new Coordinate(pts[0]);
146  
147     LinearRing ring = geomFact.createLinearRing(pts);
148     Polygon poly = geomFact.createPolygon(ring);
149     return poly;
150   }
151 }
152