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.jtstest.function;
14 
15 import java.awt.Font;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.List;
19 
20 import org.locationtech.jts.algorithm.Angle;
21 import org.locationtech.jts.awt.*;
22 import org.locationtech.jts.geom.*;
23 import org.locationtech.jts.geom.util.*;
24 import org.locationtech.jts.util.GeometricShapeFactory;
25 import org.locationtech.jtstest.geomfunction.Metadata;
26 
27 public class CreateShapeFunctions {
28 
29 
30 	private static final int DEFAULT_POINTSIZE = 100;
31 
fontGlyphSerif(Geometry g, String text)32         public static Geometry fontGlyphSerif(Geometry g, String text)
33         {
34                 return fontGlyph(g, text, new Font(FontGlyphReader.FONT_SERIF, Font.PLAIN, DEFAULT_POINTSIZE));
35         }
36 
fontGlyphSerifPointSize(Geometry g, String text, @Metadata(title=R) int pointSize)37         public static Geometry fontGlyphSerifPointSize(Geometry g, String text,
38             @Metadata(title="Point size")
39             int pointSize)
40         {
41                 return fontGlyph(g, text, new Font(FontGlyphReader.FONT_SERIF, Font.PLAIN, pointSize));
42         }
43 
fontGlyph(Geometry g, String text, @Metadata(title=R) String fontName)44         public static Geometry fontGlyph(Geometry g, String text,
45             @Metadata(title="Font name")
46             String fontName)
47         {
48                 return fontGlyph(g, text, new Font(fontName, Font.PLAIN, DEFAULT_POINTSIZE));
49         }
50 
fontGlyphSansSerif(Geometry g, String text)51 	public static Geometry fontGlyphSansSerif(Geometry g, String text)
52 	{
53 		return fontGlyph(g, text, new Font(FontGlyphReader.FONT_SANSSERIF, Font.PLAIN, DEFAULT_POINTSIZE));
54 	}
55 
fontGlyphMonospaced(Geometry g, String text)56 	public static Geometry fontGlyphMonospaced(Geometry g, String text)
57 	{
58 		return fontGlyph(g, text, new Font(FontGlyphReader.FONT_MONOSPACED, Font.PLAIN, DEFAULT_POINTSIZE));
59 	}
60 
fontGlyph(Geometry g, String text, Font font)61 	private static Geometry fontGlyph(Geometry g, String text, Font font) {
62 		Envelope env = FunctionsUtil.getEnvelopeOrDefault(g);
63 		GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g);
64 
65 		Geometry textGeom = FontGlyphReader.read(text, font, geomFact);
66 		Envelope envText = textGeom.getEnvelopeInternal();
67 
68 		if (g != null) {
69 			// transform to baseline
70 			Coordinate baseText0 = new Coordinate(envText.getMinX(), envText.getMinY());
71 			Coordinate baseText1 = new Coordinate(envText.getMaxX(), envText.getMinY());
72 			Coordinate baseGeom0 = new Coordinate(env.getMinX(), env.getMinY());
73 			Coordinate baseGeom1 = new Coordinate(env.getMaxX(), env.getMinY());
74 			AffineTransformation trans = AffineTransformationFactory.createFromBaseLines(baseText0, baseText1, baseGeom0, baseGeom1);
75 			return trans.transform(textGeom);
76 		}
77 		return textGeom;
78 	}
79 
grid(Geometry g, int nCells)80   public static Geometry grid(Geometry g, int nCells)
81   {
82     Envelope env = FunctionsUtil.getEnvelopeOrDefault(g);
83     GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g);
84 
85     int nCellsOnSideY = (int) Math.sqrt(nCells);
86     int nCellsOnSideX = nCells / nCellsOnSideY;
87 
88     // alternate: make square cells, with varying grid width/height
89     //double extent = env.minExtent();
90     //double nCellsOnSide = Math.max(nCellsOnSideY, nCellsOnSideX);
91 
92     double cellSizeX = env.getWidth() / nCellsOnSideX;
93     double cellSizeY = env.getHeight() / nCellsOnSideY;
94 
95     List geoms = new ArrayList();
96 
97     for (int i = 0; i < nCellsOnSideX; i++) {
98       for (int j = 0; j < nCellsOnSideY; j++) {
99         double x = env.getMinX() + i * cellSizeX;
100         double y = env.getMinY() + j * cellSizeY;
101         double x2 = env.getMinX() + (i + 1) * cellSizeX;
102         double y2 = env.getMinY() + (j + 1) * cellSizeY;
103 
104         Envelope cellEnv = new Envelope(x, x2, y, y2);
105         geoms.add(geomFact.toGeometry(cellEnv));
106       }
107     }
108     return geomFact.buildGeometry(geoms);
109   }
110 
gridPoints(Geometry g, int nCells)111   public static Geometry gridPoints(Geometry g, int nCells)
112   {
113     Envelope env = FunctionsUtil.getEnvelopeOrDefault(g);
114     GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g);
115 
116     int nCellsOnSideY = (int) Math.sqrt(nCells);
117     int nCellsOnSideX = nCells / nCellsOnSideY;
118 
119     double cellSizeX = env.getWidth() / (nCellsOnSideX - 1);
120     double cellSizeY = env.getHeight() / (nCellsOnSideY - 1);
121 
122     CoordinateList pts = new CoordinateList();
123 
124     for (int i = 0; i < nCellsOnSideX; i++) {
125       for (int j = 0; j < nCellsOnSideY; j++) {
126         double x = env.getMinX() + i * cellSizeX;
127         double y = env.getMinY() + j * cellSizeY;
128 
129         pts.add( new Coordinate(x, y) );
130       }
131     }
132     return geomFact.createMultiPointFromCoords(pts.toCoordinateArray());
133   }
134 
supercircle3(Geometry g, int nPts)135 	public static Geometry supercircle3(Geometry g, int nPts)
136 	{
137 		return supercircle(g, nPts, 3);
138 	}
139 
squircle(Geometry g, int nPts)140 	public static Geometry squircle(Geometry g, int nPts)
141 	{
142 		return supercircle(g, nPts, 4);
143 	}
144 
supercircle5(Geometry g, int nPts)145 	public static Geometry supercircle5(Geometry g, int nPts)
146 	{
147 		return supercircle(g, nPts, 5);
148 	}
149 
supercirclePoint5(Geometry g, int nPts)150 	public static Geometry supercirclePoint5(Geometry g, int nPts)
151 	{
152 		return supercircle(g, nPts, 0.5);
153 	}
154 
155 
supercircle(Geometry g, @Metadata(title=R) int nPts, @Metadata(title=R) double pow)156 	public static Geometry supercircle(Geometry g,
157 	    @Metadata(title="Point count")
158 	    int nPts,
159 	    @Metadata(title="Power")
160 	    double pow)
161 	{
162 		GeometricShapeFactory gsf = new GeometricShapeFactory();
163 		gsf.setNumPoints(nPts);
164 		if (g != null)
165 			gsf.setEnvelope(g.getEnvelopeInternal());
166 		else
167 			gsf.setEnvelope(new Envelope(0, 1, 0, 1));
168 		return gsf.createSupercircle(pow);
169 	}
170 
ellipse(Geometry g, int nPts)171   public static Geometry ellipse(Geometry g, int nPts)
172   {
173     GeometricShapeFactory gsf = new GeometricShapeFactory();
174     gsf.setNumPoints(nPts);
175     if (g != null)
176       gsf.setEnvelope(g.getEnvelopeInternal());
177     else
178       gsf.setEnvelope(new Envelope(0, 1, 0, 1));
179     return gsf.createCircle();
180   }
ellipseRotate(Geometry g, int nPts, @Metadata(title=R) double ang)181   public static Geometry ellipseRotate(Geometry g, int nPts,
182       @Metadata(title="Angle")
183       double ang)
184   {
185     GeometricShapeFactory gsf = new GeometricShapeFactory();
186     gsf.setNumPoints(nPts);
187     gsf.setRotation(ang);
188     if (g != null)
189       gsf.setEnvelope(g.getEnvelopeInternal());
190     else
191       gsf.setEnvelope(new Envelope(0, 1, 0, 1));
192     return gsf.createCircle();
193   }
194 
sineStar(Geometry g, @Metadata(title=R) int nArms, @Metadata(title=R) int nPts)195   public static Geometry sineStar(Geometry g,
196       @Metadata(title="Arm count")
197       int nArms,
198       @Metadata(title="Point count")
199       int nPts)
200   {
201 	Envelope env = FunctionsUtil.getEnvelopeOrDefault(g);
202 	GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g);
203 
204 	double size = Math.min(env.getHeight(),  env.getWidth());
205     SineStarFactory shape = new SineStarFactory(geomFact);
206     shape.setCentre(env.centre());
207     shape.setSize(size);
208     shape.setNumPoints(nPts);
209     shape.setNumArms(nArms);
210     shape.setArmLengthRatio(0.5);
211     return shape.createSineStar();
212   }
213 
comb(Geometry g, int nArms)214   public static Geometry comb(Geometry g, int nArms)
215   {
216 	Envelope env = FunctionsUtil.getEnvelopeOrDefault(g);
217 	GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(g);
218 
219 	int npts = 4 * (nArms - 1) + 2 + 2 + 1;
220 	Coordinate[] pts = new Coordinate[npts];
221 	double armWidth = env.getWidth() / (2 * nArms - 1);
222 	double armLen = env.getHeight() - armWidth;
223 
224 	double xBase = env.getMinX();
225 	double yBase = env.getMinY();
226 
227 	int ipts = 0;
228 	for (int i = 0; i < nArms; i++) {
229 		double x1 = xBase + i * 2 * armWidth;
230 		double y1 = yBase + armLen + armWidth;
231 		pts[ipts++] = new Coordinate(x1, y1);
232 		pts[ipts++] = new Coordinate(x1 + armWidth, y1);
233 		if (i < nArms - 1) {
234 			pts[ipts++] = new Coordinate(x1 + armWidth, yBase + armWidth);
235 			pts[ipts++] = new Coordinate(x1 + 2 * armWidth, yBase + armWidth);
236 		}
237 	}
238 	pts[ipts++] = new Coordinate(env.getMaxX(), yBase);
239 	pts[ipts++] = new Coordinate(xBase, yBase);
240 	pts[ipts++] = new Coordinate(pts[0]);
241 
242 	return geomFact.createPolygon(pts);
243   }
244 
pointFieldCentroidStar(Geometry ptsGeom)245   public static Geometry pointFieldCentroidStar(Geometry ptsGeom)
246   {
247     Coordinate[] pts = ptsGeom.getCoordinates();
248     Geometry centroid = ptsGeom.getCentroid();
249     return pointFieldStar(ptsGeom, centroid);
250   }
251 
pointFieldStar(Geometry ptsGeom, Geometry centrePt)252   public static Geometry pointFieldStar(Geometry ptsGeom, Geometry centrePt)
253   {
254     Coordinate[] pts = ptsGeom.getCoordinates();
255     Coordinate centre = centrePt.getCoordinate();
256 
257     List<OrderedPoint> orderedPts = new ArrayList<OrderedPoint>();
258     for (Coordinate p : pts) {
259       double ang = Angle.angle(centre, p);
260       orderedPts.add(new OrderedPoint(p, ang));
261     }
262     Collections.sort(orderedPts);
263     int n = pts.length+1;
264     Coordinate[] ring = new Coordinate[n];
265     int i = 0;
266     for (OrderedPoint op : orderedPts) {
267       ring[i++] = op.pt;
268     }
269     // close ring
270     ring[n-1] = ring[0].copy();
271     return ptsGeom.getFactory().createPolygon(ring);
272   }
273 
274   private static class OrderedPoint implements Comparable {
275     Coordinate pt;
276     double index;
277 
OrderedPoint(Coordinate p, double index)278     public OrderedPoint(Coordinate p, double index) {
279       this.pt = p;
280       this.index = index;
281     }
282 
283     @Override
compareTo(Object o)284     public int compareTo(Object o) {
285       OrderedPoint other = (OrderedPoint) o;
286       return Double.compare(index,  other.index);
287     }
288   }
289 
290   @Metadata(description="Construct a spiral")
spiral(Geometry geom, @Metadata(title=R) int nCycles, @Metadata(title=R) int quadrantSegs)291   public static Geometry spiral(Geometry geom,
292       @Metadata(title="Num Cycles")
293       int nCycles,
294       @Metadata(title="Quadrant Segs")
295       int quadrantSegs) {
296     Envelope env = FunctionsUtil.getEnvelopeOrDefault(geom);
297     GeometryFactory geomFact = FunctionsUtil.getFactoryOrDefault(geom);
298 
299     double width = Math.min(env.getHeight(), env.getWidth())/2;
300     double pitch = width / nCycles;
301 
302     Coordinate centre = env.centre();
303 
304     CoordinateList inside = new CoordinateList();
305     CoordinateList outside = new CoordinateList();
306     for (int i = 1; i <= nCycles; i++) {
307       Coordinate[] inCycle = genSpiralCycle(centre, i * pitch - pitch/2, (i+1) * pitch - pitch/2, quadrantSegs);
308       inside.add(inCycle, false);
309       Coordinate[] outCycle = genSpiralCycle(centre, i * pitch, (i+1) * pitch, quadrantSegs);
310       outside.add(outCycle, false);
311     }
312     CoordinateList all = new CoordinateList();
313     all.add(inside.toCoordinateArray(), false);
314     Coordinate[] outsidePts = outside.toCoordinateArray();
315     CoordinateArrays.reverse(outsidePts);
316     all.add(outsidePts, false);
317     all.closeRing();
318     return geomFact.createPolygon(all.toCoordinateArray());
319   }
320 
genSpiralCycle(Coordinate centre, double radiusStart, double radiusEnd, int quadrantSegs)321   private static Coordinate[] genSpiralCycle(Coordinate centre,
322       double radiusStart, double radiusEnd, int quadrantSegs) {
323     int nPts = quadrantSegs * 4 + 1;
324     Coordinate[] pts = new Coordinate[nPts];
325     double angInc = 2 * Math.PI / (nPts - 1);
326     double radiusInc = (radiusEnd - radiusStart) / (nPts - 1);
327     for (int i = 0; i < nPts; i++) {
328       double radius = radiusStart + i * radiusInc;
329       double x = radius * Math.cos(i *angInc);
330       double y = radius * Math.sin(i *angInc);
331       Coordinate pt = new Coordinate(centre.getX() + x, centre.getY() + y);
332       pts[i] = pt;
333     }
334     return pts;
335   }
336 }
337