Statistics
| Revision:

root / trunk / libraries / libTopology / src / com / graphbuilder / curve / ShapeMultiPath.java @ 37940

History | View | Annotate | Download (9.16 KB)

1
package com.graphbuilder.curve;
2

    
3
import java.awt.Rectangle;
4
import java.awt.Shape;
5
import java.awt.geom.AffineTransform;
6
import java.awt.geom.PathIterator;
7
import java.awt.geom.Point2D;
8
import java.awt.geom.Rectangle2D;
9

    
10
import com.graphbuilder.geom.Geom;
11

    
12
/**
13
The ShapeMultiPath is-a MultiPath and implements the java.awt.Shape interface.
14
Here is an example of how to use a ShapeMultiPath:
15

16
<pre>
17
ControlPath cp = new ControlPath();
18
cp.addPoint(...); // add points
19
Curve c = new BezierCurve(cp, new GroupIterator("0:n-1"));
20

21
ShapeMultiPath smp = new ShapeMultiPath();
22
c.appendTo(smp);
23

24
Graphics2D g = ...;
25
g.draw(smp);
26

27
</pre>
28
*/
29
public class ShapeMultiPath extends MultiPath implements Shape {
30

    
31
        private int windingRule = PathIterator.WIND_EVEN_ODD;
32
        private int ai0 = 0;
33
        private int ai1 = 1;
34

    
35
        /**
36
        Constructs a new ShapeMultiPath with a dimension of 2.
37
        */
38
        public ShapeMultiPath() {
39
                super(2);
40
        }
41

    
42
        /**
43
        Constructs a new ShapeMultiPath with the specified dimension requirement.
44

45
        @throws IllegalArgumentException If the specified dimension is less than 2.
46
        */
47
        public ShapeMultiPath(int dimension) {
48
                super(dimension);
49

    
50
                if (dimension < 2)
51
                        throw new IllegalArgumentException("dimension >= 2 required");
52
        }
53

    
54
        /**
55
        The basis vectors specify which index corresponds to the x-axis and which index
56
        corresponds to the y-axis.  The value of the x-axis is at index location 0 and the
57
        value of the y-axis is at index location 1.
58

59
        @throws IllegalArgumentException If the axis values are less than 0 or greater than or
60
        equal to the dimension.
61

62
        @see #getBasisVectors()
63
        */
64
        public void setBasisVectors(int[] b) {
65
                int b0 = b[0];
66
                int b1 = b[1];
67

    
68
                int dimension = getDimension();
69

    
70
                if (b0 < 0 || b1 < 0 || b0 >= dimension || b1 >= dimension)
71
                        throw new IllegalArgumentException("basis vectors must be >= 0 and < dimension");
72

    
73
                ai0 = b0;
74
                ai1 = b1;
75
        }
76

    
77
        /**
78
        Returns a new integer array with the basis vectors.  The default basis vectors are {0, 1}.
79

80
        @see #setBasisVectors(int[])
81
        */
82
        public int[] getBasisVectors() {
83
                return new int[] { ai0, ai1 };
84
        }
85

    
86
        /**
87
        Returns the minimum distance^2 from the specified point to the line segments of this multi-path.
88
        */
89
        public double getDistSq(double x, double y) {
90
                int n = getNumPoints();
91

    
92
                if (n == 0)
93
                        return Double.MAX_VALUE;
94

    
95
                double[] p = get(0);
96

    
97
                double x2 = p[ai0];
98
                double y2 = p[ai1];
99
                double dist = Double.MAX_VALUE;
100

    
101
                for (int i = 1; i < n; i++) {
102
                        p = get(i);
103
                        double x1 = p[ai0];
104
                        double y1 = p[ai1];
105

    
106
                        if (getType(i) == MultiPath.LINE_TO) {
107
                                double d = Geom.ptSegDistSq(x1, y1, x2, y2, x, y, null);
108
                                if (d < dist)
109
                                        dist = d;
110
                        }
111

    
112
                        x2 = x1;
113
                        y2 = y1;                        
114
                }
115

    
116
                return dist;
117
        }
118

    
119

    
120
        //------------------------------------------------------------------------------------------
121
        // methods for Shape interface:
122

    
123

    
124
        /**
125
        Returns the value of the winding rule. The default value is PathIterator.WIND_EVEN_ODD.
126

127
        @see #setWindingRule(int)
128
        */
129
        public int getWindingRule() {
130
                return windingRule;
131
        }
132

    
133
        /**
134
        Sets the winding rule.  The winding rule can either by PathIterator.WIND_EVEN_ODD or
135
        PathIterator.WIND_NON_ZERO, otherwise an IllegalArgumentException is thrown.
136
        */
137
        public void setWindingRule(int rule) {
138
                if (rule != PathIterator.WIND_EVEN_ODD && rule != PathIterator.WIND_NON_ZERO)
139
                        throw new IllegalArgumentException("winding rule must be WIND_EVEN_ODD or WIND_NON_ZERO");
140

    
141
                windingRule = rule;
142
        }
143

    
144
        /**
145
        Returns a new PathIterator object.
146
        */
147
        public PathIterator getPathIterator(AffineTransform at) {
148
                return new ShapeMultiPathIterator(this, at);
149
        }
150

    
151
        /**
152
        Returns a new PathIterator object.  The flatness parameter is ignored since a multi-path, by
153
        definition, is already flat.
154
        */
155
        public PathIterator getPathIterator(AffineTransform at, double flatness) {
156
                return new ShapeMultiPathIterator(this, at);
157
        }
158

    
159
        //---------------------------------------------------------------
160

    
161
        /**
162
        See the getBounds2D() method.
163

164
        @see #getBounds2D()
165
        */
166
        public Rectangle getBounds() {
167
                Rectangle2D r = getBounds2D();
168
                if (r == null) return null;
169
                return r.getBounds();
170
        }
171

    
172
        /**
173
        Computes the bounding box of the points.  When computing the bounding box, a point is considered if it is
174
        of type LINE_TO or it is of type MOVE_TO and the next point is of type LINE_TO.  A value of null is
175
        returned if there is not enough data to define a bounding box.
176
        */
177
        public Rectangle2D getBounds2D() {
178
                int n = getNumPoints();
179

    
180
                double x1 = Double.MAX_VALUE;
181
                double y1 = Double.MAX_VALUE;
182
                double x2 = -Double.MAX_VALUE;
183
                double y2 = -Double.MAX_VALUE;
184

    
185
                boolean defined = false;
186

    
187
                for (int i = 0; i < n; i++) {
188
                        double[] p = get(i);
189

    
190
                        boolean b = false;
191

    
192
                        if (getType(i) == MultiPath.MOVE_TO) {
193
                                if (i < n - 1 && getType(i+1) == MultiPath.LINE_TO)
194
                                        b = true;
195
                        }
196
                        else {
197
                                b = true;
198
                        }
199

    
200
                        if (b) {
201
                                defined = true;
202
                                if (p[ai0] < x1) x1 = p[ai0];
203
                                if (p[ai1] < y1) y1 = p[ai1];
204
                                if (p[ai0] > x2) x2 = p[ai0];
205
                                if (p[ai1] > y2) y2 = p[ai1];
206
                        }
207
                }
208

    
209
                if (!defined)
210
                        return null;
211

    
212
                return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
213
        }
214

    
215

    
216
        //---------------------------------------------------------------
217

    
218
        /**
219
        Returns true if the point is contained inside the shape. Otherwise false is returned.
220
        */
221
        public boolean contains(double x, double y) {
222
                int cross = org.gvsig.geoutils.sun.awt.geom.Curve.pointCrossingsForPath(getPathIterator(null), x, y);
223

    
224
                if (windingRule == PathIterator.WIND_NON_ZERO)
225
                        return cross != 0;
226

    
227
                return (cross & 1) != 0;
228
        }
229

    
230
        /**
231
        See the contains(x, y) method.
232

233
        @see #contains(double,double)
234
        */
235
        public boolean contains(Point2D p) {
236
                return contains(p.getX(), p.getY());
237
        }
238

    
239
        /**
240
        Returns true only if the shape contains all points of the rectangle. First, if any of
241
        the four corners is not contained in the shape then false is returned.  Now we know
242
        that all four corners are inside the shape.  Next, we check to see if any line segment
243
        of this shape intersects any of the 4 line segments formed by the rectangle.  If there
244
        is an intersection, then false is returned.  Otherwise true is returned.
245
        */
246
        public boolean contains(double x1, double y1, double w, double h) {
247
                double x2 = x1 + w;
248
                double y2 = y1 + h;
249

    
250
                if (!contains(x1, y1)) return false;
251
                if (!contains(x1, y2)) return false;
252
                if (!contains(x2, y1)) return false;
253
                if (!contains(x2, y2)) return false;
254

    
255
                int n = getNumPoints();
256

    
257
                if (n == 0) return false;
258

    
259
                double[] p = get(0);
260

    
261
                double xb = p[ai0];
262
                double yb = p[ai1];
263

    
264
                for (int i = 1; i < n; i++) {
265
                        p = get(i);
266
                        double xa = p[ai0];
267
                        double ya = p[ai1];
268

    
269
                        if (getType(i) == MultiPath.LINE_TO) {
270
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y1, x2, y1, null) == Geom.INTERSECT)
271
                                        return false;
272
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y1, x1, y2, null) == Geom.INTERSECT)
273
                                        return false;
274
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y2, x2, y2, null) == Geom.INTERSECT)
275
                                        return false;
276
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x2, y1, x2, y2, null) == Geom.INTERSECT)
277
                                        return false;
278
                        }
279

    
280
                        xb = xa;
281
                        yb = ya;
282
                }
283

    
284
                return true;
285
        }
286

    
287
        /**
288
        See the contains(x, y, w, h) method.
289

290
        @see #contains(double,double,double,double)
291
        */
292
        public boolean contains(Rectangle2D r) {
293
                return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
294
        }
295
        //---------------------------------------------------------------
296

    
297
        /**
298
        This method returns true if any line segment in this multi-path intersects any of the
299
        4 line segments formed by the rectangle or any corner of the rectangle is inside the
300
        shape or any point of the shape is inside the rectangle.  Otherwise false is returned.
301
        */
302
        public boolean intersects(double x1, double y1, double w, double h) {
303
                double x2 = x1 + w;
304
                double y2 = y1 + h;
305

    
306
                if (contains(x1, y1)) return true;
307
                if (contains(x1, y2)) return true;
308
                if (contains(x2, y1)) return true;
309
                if (contains(x2, y2)) return true;
310

    
311
                int n = getNumPoints();
312

    
313
                if (n == 0) return false;
314

    
315
                double[] p = get(0);
316

    
317
                double xb = p[ai0];
318
                double yb = p[ai1];
319

    
320
                for (int i = 1; i < n; i++) {
321
                        p = get(i);
322
                        double xa = p[ai0];
323
                        double ya = p[ai1];
324

    
325
                        if (getType(i) == MultiPath.LINE_TO) {
326
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y1, x2, y1, null) == Geom.INTERSECT)
327
                                        return true;
328
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y1, x1, y2, null) == Geom.INTERSECT)
329
                                        return true;
330
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y2, x2, y2, null) == Geom.INTERSECT)
331
                                        return true;
332
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x2, y1, x2, y2, null) == Geom.INTERSECT)
333
                                        return true;
334

    
335
                                if (xa >= x1 && ya >= y1 && xa <= x2 && ya <= y2) return true;
336
                                if (xb >= x1 && yb >= y1 && xb <= x2 && yb <= y2) return true;
337
                        }
338

    
339
                        xb = xa;
340
                        yb = ya;
341
                }
342

    
343
                return false;
344
        }
345

    
346
        /**
347
        See the intersects(x, y, w, h) method.
348

349
        @see #intersects(double,double,double,double)
350
        */
351
        public boolean intersects(Rectangle2D r) {
352
                return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
353
        }
354
}