View Javadoc

1   /**
2    * 
3    */
4   
5   package math.geom2d.grid;
6   
7   import java.util.ArrayList;
8   import java.util.Collection;
9   
10  import math.geom2d.Box2D;
11  import math.geom2d.Point2D;
12  import math.geom2d.point.PointArray2D;
13  import math.geom2d.point.PointSet2D;
14  import math.geom2d.line.LineSegment2D;
15  import math.geom2d.line.StraightLine2D;
16  import math.geom2d.line.LinearShape2D;
17  
18  /**
19   * Defines a triangle grid, with various size and orientation. The grid contains
20   * triangle with all edges the same length.
21   * 
22   * @author dlegland
23   */
24  public class TriangleGrid2D implements Grid2D {
25  
26      double x0    = 0;
27      double y0    = 0;
28      double s     = 1;
29  
30      double theta = 0;
31  
32      /**
33       * Returns TRUE if the number <code>n</code> is even (like 0, 2, 4...).
34       * 
35       * @param n an integer
36       * @return TRUE if n is even.
37       */
38      private final static boolean isEven(int n) {
39          return Math.abs(n*.5-Math.floor(n*.5))<.25;
40      }
41  
42      public TriangleGrid2D() {
43          this(0, 0, 1, 0);
44      }
45  
46      /**
47       * @param s size of the triangle tile
48       */
49      public TriangleGrid2D(double s) {
50          this(0, 0, s, 0);
51      }
52  
53      /**
54       * @param x0 x-coord of grid origin
55       * @param y0 y-coord of grid origin
56       */
57      public TriangleGrid2D(double x0, double y0) {
58          this(x0, y0, 1, 0);
59      }
60  
61      /**
62       * @param x0 x-coord of grid origin
63       * @param y0 y-coord of grid origin
64       * @param s size of the triangle tile
65       */
66      public TriangleGrid2D(double x0, double y0, double s) {
67          this(x0, y0, s, 0);
68      }
69  
70      /**
71       * @param x0 x-coord of grid origin
72       * @param y0 y-coord of grid origin
73       * @param s size of the triangle tile
74       * @param theta orientation of the grid with horizontal
75       */
76      public TriangleGrid2D(double x0, double y0, double s, double theta) {
77          this.x0 = x0;
78          this.y0 = y0;
79          this.s = s;
80          this.theta = theta;
81      }
82  
83      /**
84       * Assumes unit grid.
85       * 
86       * @param point the grid origin
87       */
88      public TriangleGrid2D(Point2D point) {
89          this(point.getX(), point.getY(), 1, 0);
90      }
91  
92      /**
93       * @param point the grid origin
94       * @param s size of the triangle tile
95       */
96      public TriangleGrid2D(Point2D point, double s) {
97          this(point.getX(), point.getY(), s, 0);
98      }
99  
100     /**
101      * @param point the grid origin
102      * @param s size of the triangle tile
103      * @param theta orientation of the grid with horizontal
104      */
105     public TriangleGrid2D(Point2D point, double s, double theta) {
106         this(point.getX(), point.getY(), s, theta);
107     }
108 
109     /**
110      * @deprecated grids are supposed to be immutable (0.8.0)
111      */
112     @Deprecated
113     public void setOrigin(Point2D point) {
114         this.x0 = point.getX();
115         this.y0 = point.getY();
116     }
117 
118     public Point2D getOrigin() {
119         return new Point2D(x0, y0);
120     }
121 
122     public double getSize() {
123         return s;
124     }
125 
126     /**
127      * @deprecated grids are supposed to be immutable (0.8.0)
128      */
129     @Deprecated
130     public void setSize(double s) {
131         this.s = s;
132     }
133 
134     /**
135      * @deprecated grids are supposed to be immutable (0.8.0)
136      */
137     @Deprecated
138     public void setAngle(double theta) {
139         this.theta = theta;
140     }
141 
142     public double getTheta() {
143         return theta;
144     }
145 
146     /*
147      * (non-Javadoc)
148      * 
149      * @see math.geom2d.grid.Grid2D#getClosestVertex(java.awt.geom.Point2D)
150      */
151     public Point2D getClosestVertex(java.awt.geom.Point2D point) {
152         // create the base line
153         double cot = Math.cos(theta);
154         double sit = Math.sin(theta);
155         StraightLine2D baseLine = new StraightLine2D(x0, y0, cot, sit);
156 
157         // compute distance to line, and deduces indices of surrounding lines
158         double s2 = s*Math.sqrt(3)/2;
159         double d = baseLine.getSignedDistance(point);
160         int n1 = (int) Math.floor(d/s2);
161         int n2 = (int) Math.ceil(d/s2);
162 
163         // compute the two surrounding lines
164         StraightLine2D line1 = baseLine.getParallel(n1*s2);
165         StraightLine2D line2 = baseLine.getParallel(n2*s2);
166 
167         // projection of point on the surrounding lines
168         double t = line1.project(new Point2D(point));
169 
170         Point2D p1, p2, p3;
171         if (isEven(n1)) {
172             p1 = line1.getPoint(Math.floor(t/s)*s);
173             p2 = line1.getPoint(Math.ceil(t/s)*s);
174             p3 = line2.getPoint((Math.floor(t/s)+.5)*s);
175         } else {
176             p1 = line1.getPoint((Math.floor(t/s)+.5)*s);
177             p2 = line2.getPoint(Math.floor(t/s)*s);
178             p3 = line2.getPoint(Math.ceil(t/s)*s);
179         }
180 
181         Point2D res = p1;
182         double minDist = res.getDistance(point);
183 
184         double d2 = p2.getDistance(point);
185         if (d2<minDist) {
186             res = p2;
187             minDist = d2;
188         }
189 
190         double d3 = p3.getDistance(point);
191         if (d3<minDist) {
192             res = p3;
193             minDist = d3;
194         }
195         return res;
196     }
197 
198     /*
199      * (non-Javadoc)
200      * 
201      * @see math.geom2d.grid.Grid2D#getEdges(math.geom2d.Box2D)
202      */
203     public Collection<LineSegment2D> getEdges(Box2D box) {
204 
205         // init the array of line segments
206         ArrayList<LineSegment2D> array = new ArrayList<LineSegment2D>();
207 
208         double d = s*Math.sqrt(3)/2;
209         double dmin, dmax;
210 
211         for (int k = 0; k<3; k++) {
212             // consider a line through origin with one of the 2 orientations
213             double theta2 = this.theta+Math.PI*(k)/3.0;
214             double cot = Math.cos(theta2);
215             double sit = Math.sin(theta2);
216             StraightLine2D baseLine = new StraightLine2D(x0, y0, cot, sit);
217 
218             // get extreme distances of box corners to the base line
219             dmin = Double.POSITIVE_INFINITY;
220             dmax = Double.NEGATIVE_INFINITY;
221             for (Point2D point : box.getVertices()) {
222                 double dist = baseLine.getSignedDistance(point);
223                 dmin = Math.min(dmin, dist);
224                 dmax = Math.max(dmax, dist);
225             }
226 
227             // compute the number of lines in each direction
228             double s2 = s*Math.sqrt(3)/2;
229             int i0 = (int) Math.ceil(dmin/s2);
230             int i1 = (int) Math.floor(dmax/s2);
231 
232             // add each clipped line
233             for (int i = i0; i<=i1; i++) {
234                 StraightLine2D line = baseLine.getParallel(d*i);
235                 for (LinearShape2D arc : line.clip(box)) {
236                     if (arc instanceof LineSegment2D)
237                         array.add((LineSegment2D) arc);
238                 }
239             }
240         }
241         return array;
242     }
243 
244     /*
245      * (non-Javadoc)
246      * 
247      * @see math.geom2d.grid.Grid2D#getVertices(math.geom2d.Box2D)
248      */
249     public PointSet2D getVertices(Box2D box) {
250 
251         // init the array of line segments
252         ArrayList<Point2D> array = new ArrayList<Point2D>();
253 
254         double d = s*Math.sqrt(3)/2;
255         double dmin, dmax;
256 
257         // consider a line through origin with one of the 2 orientations
258         double cot = Math.cos(theta);
259         double sit = Math.sin(theta);
260         StraightLine2D baseLine = new StraightLine2D(x0, y0, cot, sit);
261 
262         // get extreme distances of box corners to the base line
263         dmin = Double.POSITIVE_INFINITY;
264         dmax = Double.NEGATIVE_INFINITY;
265         for (Point2D point : box.getVertices()) {
266             double dist = baseLine.getSignedDistance(point);
267             dmin = Math.min(dmin, dist);
268             dmax = Math.max(dmax, dist);
269         }
270 
271         // compute the number of lines in each direction
272         int i0 = (int) Math.ceil(dmin/s);
273         int i1 = (int) Math.floor(dmax/s);
274 
275         // consider only the first line
276         for (int i = i0; i<=i1; i++) {
277             // compute supporting line, supposing that the norm of the
278             // direction vector equals 1 (should be the case)
279             StraightLine2D line = baseLine.getParallel(d*i);
280 
281             // extract the line segment
282             LineSegment2D seg = (LineSegment2D) line.clip(box).getFirstCurve();
283 
284             // compute position of extreme points, which is also the geodesic
285             // distance
286             double t1 = line.getPosition(seg.getFirstPoint());
287             double t2 = line.getPosition(seg.getLastPoint());
288 
289             // check if point on this line are shifted or not
290             double t0 = isEven(i) ? 0 : s*.5;
291 
292             // compute the number of points in each side of the origin
293             int j0 = (int) Math.ceil((t1-t0)/s);
294             int j1 = (int) Math.floor((t2-t0)/s);
295 
296             // iterate on points
297             if (j1<j0)
298                 continue;
299             for (int j = j0; j<=j1; j++)
300                 array.add(line.getPoint(j*s+t0));
301         }
302 
303         return new PointArray2D(array);
304     }
305 
306 }