|
11 | 11 | */ |
12 | 12 | package org.locationtech.jtstest.function; |
13 | 13 |
|
| 14 | +import org.locationtech.jts.algorithm.Orientation; |
14 | 15 | import org.locationtech.jts.geom.Coordinate; |
| 16 | +import org.locationtech.jts.geom.CoordinateArrays; |
15 | 17 | import org.locationtech.jts.geom.Geometry; |
16 | 18 | import org.locationtech.jts.geom.GeometryFactory; |
17 | 19 | import org.locationtech.jts.geom.LineSegment; |
@@ -112,12 +114,65 @@ public static Geometry angleBisectors(Geometry g) |
112 | 114 | return geomFact.createMultiLineString(line); |
113 | 115 | } |
114 | 116 |
|
115 | | - |
116 | 117 | private static Coordinate[] trianglePts(Geometry g) |
117 | 118 | { |
118 | | - Coordinate[] pts = g.getCoordinates(); |
| 119 | + Coordinate[] pts = CoordinateArrays.copyDeep(g.getCoordinates()); |
| 120 | + if (Orientation.isCCW(pts)) { |
| 121 | + CoordinateArrays.reverse(pts); |
| 122 | + } |
119 | 123 | if (pts.length < 3) |
120 | 124 | throw new IllegalArgumentException("Input geometry must have at least 3 points"); |
121 | 125 | return pts; |
122 | 126 | } |
| 127 | + |
| 128 | + /** |
| 129 | + * Constructs the inner hexagon of a triangle, |
| 130 | + * created by intersecting the chords of the triangle running from each vertex to |
| 131 | + * points a distance of (side / nSections) from each end of the opposite side. |
| 132 | + * |
| 133 | + * When the parameter is 3 this provides a visualization of |
| 134 | + * Marion Walter's Theorem (https://en.wikipedia.org/wiki/Marion_Walter#Recognition). |
| 135 | + * The theorem states that if each side of an arbitrary triangle is trisected |
| 136 | + * and lines are drawn to the opposite vertices, |
| 137 | + * the area of the hexagon created in the middle is one-tenth the area of the original triangle. |
| 138 | + * |
| 139 | + * @param g a triangle |
| 140 | + * @param nSections the number of sections to divide each side into (>= 3) |
| 141 | + * @return the inner hexagon |
| 142 | + */ |
| 143 | + public static Geometry innerHexagon(Geometry g, int nSections) { |
| 144 | + Coordinate[] pts = trianglePts(g); |
| 145 | + //-- return empty polygon for degenerate cases |
| 146 | + if (nSections < 3) { |
| 147 | + return g.getFactory().createEmpty(2); |
| 148 | + } |
| 149 | + |
| 150 | + LineSegment side0 = new LineSegment(pts[0], pts[1]); |
| 151 | + LineSegment side1 = new LineSegment(pts[1], pts[2]); |
| 152 | + LineSegment side2 = new LineSegment(pts[2], pts[0]); |
| 153 | + |
| 154 | + double frac = 1.0 / nSections; |
| 155 | + LineSegment chord0a = chord(pts[0], side1, frac); |
| 156 | + LineSegment chord0b = chord(pts[0], side1, 1.0 - frac); |
| 157 | + LineSegment chord1a = chord(pts[1], side2, frac); |
| 158 | + LineSegment chord1b = chord(pts[1], side2, 1.0 - frac); |
| 159 | + LineSegment chord2a = chord(pts[2], side0, frac); |
| 160 | + LineSegment chord2b = chord(pts[2], side0, 1.0 - frac); |
| 161 | + |
| 162 | + Coordinate[] hexPts = new Coordinate[7]; |
| 163 | + hexPts[0] = chord0a.intersection(chord1b); |
| 164 | + hexPts[1] = chord0a.intersection(chord2b); |
| 165 | + hexPts[2] = chord1a.intersection(chord2b); |
| 166 | + hexPts[3] = chord1a.intersection(chord0b); |
| 167 | + hexPts[4] = chord2a.intersection(chord0b); |
| 168 | + hexPts[5] = chord2a.intersection(chord1b); |
| 169 | + hexPts[6] = hexPts[0].copy(); |
| 170 | + |
| 171 | + return g.getFactory().createPolygon(hexPts); |
| 172 | + } |
| 173 | + |
| 174 | + private static LineSegment chord(Coordinate apex, LineSegment side, double frac) { |
| 175 | + Coordinate opp = side.pointAlong(frac); |
| 176 | + return new LineSegment(apex, opp); |
| 177 | + } |
123 | 178 | } |
0 commit comments