Statistics
| Revision:

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

History | View | Annotate | Download (9.02 KB)

1
package com.graphbuilder.curve;
2

    
3
import java.awt.*;
4
import java.awt.geom.*;
5
import com.graphbuilder.geom.Geom;
6

    
7
/**
8
The ShapeMultiPath is-a MultiPath and implements the java.awt.Shape interface.
9
Here is an example of how to use a ShapeMultiPath:
10

11
<pre>
12
ControlPath cp = new ControlPath();
13
cp.addPoint(...); // add points
14
Curve c = new BezierCurve(cp, new GroupIterator("0:n-1"));
15

16
ShapeMultiPath smp = new ShapeMultiPath();
17
c.appendTo(smp);
18

19
Graphics2D g = ...;
20
g.draw(smp);
21

22
</pre>
23
*/
24
public class ShapeMultiPath extends MultiPath implements Shape {
25

    
26
        private int windingRule = PathIterator.WIND_EVEN_ODD;
27
        private int ai0 = 0;
28
        private int ai1 = 1;
29

    
30
        /**
31
        Constructs a new ShapeMultiPath with a dimension of 2.
32
        */
33
        public ShapeMultiPath() {
34
                super(2);
35
        }
36

    
37
        /**
38
        Constructs a new ShapeMultiPath with the specified dimension requirement.
39

40
        @throws IllegalArgumentException If the specified dimension is less than 2.
41
        */
42
        public ShapeMultiPath(int dimension) {
43
                super(dimension);
44

    
45
                if (dimension < 2)
46
                        throw new IllegalArgumentException("dimension >= 2 required");
47
        }
48

    
49
        /**
50
        The basis vectors specify which index corresponds to the x-axis and which index
51
        corresponds to the y-axis.  The value of the x-axis is at index location 0 and the
52
        value of the y-axis is at index location 1.
53

54
        @throws IllegalArgumentException If the axis values are less than 0 or greater than or
55
        equal to the dimension.
56

57
        @see #getBasisVectors()
58
        */
59
        public void setBasisVectors(int[] b) {
60
                int b0 = b[0];
61
                int b1 = b[1];
62

    
63
                int dimension = getDimension();
64

    
65
                if (b0 < 0 || b1 < 0 || b0 >= dimension || b1 >= dimension)
66
                        throw new IllegalArgumentException("basis vectors must be >= 0 and < dimension");
67

    
68
                ai0 = b0;
69
                ai1 = b1;
70
        }
71

    
72
        /**
73
        Returns a new integer array with the basis vectors.  The default basis vectors are {0, 1}.
74

75
        @see #setBasisVectors(int[])
76
        */
77
        public int[] getBasisVectors() {
78
                return new int[] { ai0, ai1 };
79
        }
80

    
81
        /**
82
        Returns the minimum distance^2 from the specified point to the line segments of this multi-path.
83
        */
84
        public double getDistSq(double x, double y) {
85
                int n = getNumPoints();
86

    
87
                if (n == 0)
88
                        return Double.MAX_VALUE;
89

    
90
                double[] p = get(0);
91

    
92
                double x2 = p[ai0];
93
                double y2 = p[ai1];
94
                double dist = Double.MAX_VALUE;
95

    
96
                for (int i = 1; i < n; i++) {
97
                        p = get(i);
98
                        double x1 = p[ai0];
99
                        double y1 = p[ai1];
100

    
101
                        if (getType(i) == MultiPath.LINE_TO) {
102
                                double d = Geom.ptSegDistSq(x1, y1, x2, y2, x, y, null);
103
                                if (d < dist)
104
                                        dist = d;
105
                        }
106

    
107
                        x2 = x1;
108
                        y2 = y1;                        
109
                }
110

    
111
                return dist;
112
        }
113

    
114

    
115
        //------------------------------------------------------------------------------------------
116
        // methods for Shape interface:
117

    
118

    
119
        /**
120
        Returns the value of the winding rule. The default value is PathIterator.WIND_EVEN_ODD.
121

122
        @see #setWindingRule(int)
123
        */
124
        public int getWindingRule() {
125
                return windingRule;
126
        }
127

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

    
136
                windingRule = rule;
137
        }
138

    
139
        /**
140
        Returns a new PathIterator object.
141
        */
142
        public PathIterator getPathIterator(AffineTransform at) {
143
                return new ShapeMultiPathIterator(this, at);
144
        }
145

    
146
        /**
147
        Returns a new PathIterator object.  The flatness parameter is ignored since a multi-path, by
148
        definition, is already flat.
149
        */
150
        public PathIterator getPathIterator(AffineTransform at, double flatness) {
151
                return new ShapeMultiPathIterator(this, at);
152
        }
153

    
154
        //---------------------------------------------------------------
155

    
156
        /**
157
        See the getBounds2D() method.
158

159
        @see #getBounds2D()
160
        */
161
        public Rectangle getBounds() {
162
                Rectangle2D r = getBounds2D();
163
                if (r == null) return null;
164
                return r.getBounds();
165
        }
166

    
167
        /**
168
        Computes the bounding box of the points.  When computing the bounding box, a point is considered if it is
169
        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
170
        returned if there is not enough data to define a bounding box.
171
        */
172
        public Rectangle2D getBounds2D() {
173
                int n = getNumPoints();
174

    
175
                double x1 = Double.MAX_VALUE;
176
                double y1 = Double.MAX_VALUE;
177
                double x2 = -Double.MAX_VALUE;
178
                double y2 = -Double.MAX_VALUE;
179

    
180
                boolean defined = false;
181

    
182
                for (int i = 0; i < n; i++) {
183
                        double[] p = get(i);
184

    
185
                        boolean b = false;
186

    
187
                        if (getType(i) == MultiPath.MOVE_TO) {
188
                                if (i < n - 1 && getType(i+1) == MultiPath.LINE_TO)
189
                                        b = true;
190
                        }
191
                        else {
192
                                b = true;
193
                        }
194

    
195
                        if (b) {
196
                                defined = true;
197
                                if (p[ai0] < x1) x1 = p[ai0];
198
                                if (p[ai1] < y1) y1 = p[ai1];
199
                                if (p[ai0] > x2) x2 = p[ai0];
200
                                if (p[ai1] > y2) y2 = p[ai1];
201
                        }
202
                }
203

    
204
                if (!defined)
205
                        return null;
206

    
207
                return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
208
        }
209

    
210

    
211
        //---------------------------------------------------------------
212

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

    
219
                if (windingRule == PathIterator.WIND_NON_ZERO)
220
                        return cross != 0;
221

    
222
                return (cross & 1) != 0;
223
        }
224

    
225
        /**
226
        See the contains(x, y) method.
227

228
        @see #contains(double,double)
229
        */
230
        public boolean contains(Point2D p) {
231
                return contains(p.getX(), p.getY());
232
        }
233

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

    
245
                if (!contains(x1, y1)) return false;
246
                if (!contains(x1, y2)) return false;
247
                if (!contains(x2, y1)) return false;
248
                if (!contains(x2, y2)) return false;
249

    
250
                int n = getNumPoints();
251

    
252
                if (n == 0) return false;
253

    
254
                double[] p = get(0);
255

    
256
                double xb = p[ai0];
257
                double yb = p[ai1];
258

    
259
                for (int i = 1; i < n; i++) {
260
                        p = get(i);
261
                        double xa = p[ai0];
262
                        double ya = p[ai1];
263

    
264
                        if (getType(i) == MultiPath.LINE_TO) {
265
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y1, x2, y1, null) == Geom.INTERSECT)
266
                                        return false;
267
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y1, x1, y2, null) == Geom.INTERSECT)
268
                                        return false;
269
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y2, x2, y2, null) == Geom.INTERSECT)
270
                                        return false;
271
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x2, y1, x2, y2, null) == Geom.INTERSECT)
272
                                        return false;
273
                        }
274

    
275
                        xb = xa;
276
                        yb = ya;
277
                }
278

    
279
                return true;
280
        }
281

    
282
        /**
283
        See the contains(x, y, w, h) method.
284

285
        @see #contains(double,double,double,double)
286
        */
287
        public boolean contains(Rectangle2D r) {
288
                return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
289
        }
290
        //---------------------------------------------------------------
291

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

    
301
                if (contains(x1, y1)) return true;
302
                if (contains(x1, y2)) return true;
303
                if (contains(x2, y1)) return true;
304
                if (contains(x2, y2)) return true;
305

    
306
                int n = getNumPoints();
307

    
308
                if (n == 0) return false;
309

    
310
                double[] p = get(0);
311

    
312
                double xb = p[ai0];
313
                double yb = p[ai1];
314

    
315
                for (int i = 1; i < n; i++) {
316
                        p = get(i);
317
                        double xa = p[ai0];
318
                        double ya = p[ai1];
319

    
320
                        if (getType(i) == MultiPath.LINE_TO) {
321
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y1, x2, y1, null) == Geom.INTERSECT)
322
                                        return true;
323
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y1, x1, y2, null) == Geom.INTERSECT)
324
                                        return true;
325
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x1, y2, x2, y2, null) == Geom.INTERSECT)
326
                                        return true;
327
                                if (Geom.getSegSegIntersection(xa, ya, xb, yb, x2, y1, x2, y2, null) == Geom.INTERSECT)
328
                                        return true;
329

    
330
                                if (xa >= x1 && ya >= y1 && xa <= x2 && ya <= y2) return true;
331
                                if (xb >= x1 && yb >= y1 && xb <= x2 && yb <= y2) return true;
332
                        }
333

    
334
                        xb = xa;
335
                        yb = ya;
336
                }
337

    
338
                return false;
339
        }
340

    
341
        /**
342
        See the intersects(x, y, w, h) method.
343

344
        @see #intersects(double,double,double,double)
345
        */
346
        public boolean intersects(Rectangle2D r) {
347
                return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight());
348
        }
349
}