001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Denis M. Kishenko
019: * @version $Revision$
020: */package java.awt.geom;
021:
022: import java.awt.Rectangle;
023: import java.awt.Shape;
024: import java.util.NoSuchElementException;
025:
026: import org.apache.harmony.awt.gl.Crossing;
027: import org.apache.harmony.awt.internal.nls.Messages;
028:
029: public final class GeneralPath implements Shape, Cloneable {
030:
031: public static final int WIND_EVEN_ODD = PathIterator.WIND_EVEN_ODD;
032: public static final int WIND_NON_ZERO = PathIterator.WIND_NON_ZERO;
033:
034: /**
035: * The buffers size
036: */
037: private static final int BUFFER_SIZE = 10;
038:
039: /**
040: * The buffers capacity
041: */
042: private static final int BUFFER_CAPACITY = 10;
043:
044: /**
045: * The point's types buffer
046: */
047: byte[] types;
048:
049: /**
050: * The points buffer
051: */
052: float[] points;
053:
054: /**
055: * The point's type buffer size
056: */
057: int typeSize;
058:
059: /**
060: * The points buffer size
061: */
062: int pointSize;
063:
064: /**
065: * The path rule
066: */
067: int rule;
068:
069: /**
070: * The space amount in points buffer for different segmenet's types
071: */
072: static int pointShift[] = { 2, // MOVETO
073: 2, // LINETO
074: 4, // QUADTO
075: 6, // CUBICTO
076: 0 }; // CLOSE
077:
078: /*
079: * GeneralPath path iterator
080: */
081: class Iterator implements PathIterator {
082:
083: /**
084: * The current cursor position in types buffer
085: */
086: int typeIndex;
087:
088: /**
089: * The current cursor position in points buffer
090: */
091: int pointIndex;
092:
093: /**
094: * The source GeneralPath object
095: */
096: GeneralPath p;
097:
098: /**
099: * The path iterator transformation
100: */
101: AffineTransform t;
102:
103: /**
104: * Constructs a new GeneralPath.Iterator for given general path
105: * @param path - the source GeneralPath object
106: */
107: Iterator(GeneralPath path) {
108: this (path, null);
109: }
110:
111: /**
112: * Constructs a new GeneralPath.Iterator for given general path and transformation
113: * @param path - the source GeneralPath object
114: * @param at - the AffineTransform object to apply rectangle path
115: */
116: Iterator(GeneralPath path, AffineTransform at) {
117: this .p = path;
118: this .t = at;
119: }
120:
121: public int getWindingRule() {
122: return p.getWindingRule();
123: }
124:
125: public boolean isDone() {
126: return typeIndex >= p.typeSize;
127: }
128:
129: public void next() {
130: typeIndex++;
131: }
132:
133: public int currentSegment(double[] coords) {
134: if (isDone()) {
135: // awt.4B=Iterator out of bounds
136: throw new NoSuchElementException(Messages
137: .getString("awt.4B")); //$NON-NLS-1$
138: }
139: int type = p.types[typeIndex];
140: int count = GeneralPath.pointShift[type];
141: for (int i = 0; i < count; i++) {
142: coords[i] = p.points[pointIndex + i];
143: }
144: if (t != null) {
145: t.transform(coords, 0, coords, 0, count / 2);
146: }
147: pointIndex += count;
148: return type;
149: }
150:
151: public int currentSegment(float[] coords) {
152: if (isDone()) {
153: // awt.4B=Iterator out of bounds
154: throw new NoSuchElementException(Messages
155: .getString("awt.4B")); //$NON-NLS-1$
156: }
157: int type = p.types[typeIndex];
158: int count = GeneralPath.pointShift[type];
159: System.arraycopy(p.points, pointIndex, coords, 0, count);
160: if (t != null) {
161: t.transform(coords, 0, coords, 0, count / 2);
162: }
163: pointIndex += count;
164: return type;
165: }
166:
167: }
168:
169: public GeneralPath() {
170: this (WIND_NON_ZERO, BUFFER_SIZE);
171: }
172:
173: public GeneralPath(int rule) {
174: this (rule, BUFFER_SIZE);
175: }
176:
177: public GeneralPath(int rule, int initialCapacity) {
178: setWindingRule(rule);
179: types = new byte[initialCapacity];
180: points = new float[initialCapacity * 2];
181: }
182:
183: public GeneralPath(Shape shape) {
184: this (WIND_NON_ZERO, BUFFER_SIZE);
185: PathIterator p = shape.getPathIterator(null);
186: setWindingRule(p.getWindingRule());
187: append(p, false);
188: }
189:
190: public void setWindingRule(int rule) {
191: if (rule != WIND_EVEN_ODD && rule != WIND_NON_ZERO) {
192: // awt.209=Invalid winding rule value
193: throw new java.lang.IllegalArgumentException(Messages
194: .getString("awt.209")); //$NON-NLS-1$
195: }
196: this .rule = rule;
197: }
198:
199: public int getWindingRule() {
200: return rule;
201: }
202:
203: /**
204: * Checks points and types buffer size to add pointCount points. If necessary realloc buffers to enlarge size.
205: * @param pointCount - the point count to be added in buffer
206: */
207: void checkBuf(int pointCount, boolean checkMove) {
208: if (checkMove && typeSize == 0) {
209: // awt.20A=First segment should be SEG_MOVETO type
210: throw new IllegalPathStateException(Messages
211: .getString("awt.20A")); //$NON-NLS-1$
212: }
213: if (typeSize == types.length) {
214: byte tmp[] = new byte[typeSize + BUFFER_CAPACITY];
215: System.arraycopy(types, 0, tmp, 0, typeSize);
216: types = tmp;
217: }
218: if (pointSize + pointCount > points.length) {
219: float tmp[] = new float[pointSize
220: + Math.max(BUFFER_CAPACITY * 2, pointCount)];
221: System.arraycopy(points, 0, tmp, 0, pointSize);
222: points = tmp;
223: }
224: }
225:
226: public void moveTo(float x, float y) {
227: if (typeSize > 0
228: && types[typeSize - 1] == PathIterator.SEG_MOVETO) {
229: points[pointSize - 2] = x;
230: points[pointSize - 1] = y;
231: } else {
232: checkBuf(2, false);
233: types[typeSize++] = PathIterator.SEG_MOVETO;
234: points[pointSize++] = x;
235: points[pointSize++] = y;
236: }
237: }
238:
239: public void lineTo(float x, float y) {
240: checkBuf(2, true);
241: types[typeSize++] = PathIterator.SEG_LINETO;
242: points[pointSize++] = x;
243: points[pointSize++] = y;
244: }
245:
246: public void quadTo(float x1, float y1, float x2, float y2) {
247: checkBuf(4, true);
248: types[typeSize++] = PathIterator.SEG_QUADTO;
249: points[pointSize++] = x1;
250: points[pointSize++] = y1;
251: points[pointSize++] = x2;
252: points[pointSize++] = y2;
253: }
254:
255: public void curveTo(float x1, float y1, float x2, float y2,
256: float x3, float y3) {
257: checkBuf(6, true);
258: types[typeSize++] = PathIterator.SEG_CUBICTO;
259: points[pointSize++] = x1;
260: points[pointSize++] = y1;
261: points[pointSize++] = x2;
262: points[pointSize++] = y2;
263: points[pointSize++] = x3;
264: points[pointSize++] = y3;
265: }
266:
267: public void closePath() {
268: if (typeSize == 0
269: || types[typeSize - 1] != PathIterator.SEG_CLOSE) {
270: checkBuf(0, true);
271: types[typeSize++] = PathIterator.SEG_CLOSE;
272: }
273: }
274:
275: public void append(Shape shape, boolean connect) {
276: PathIterator p = shape.getPathIterator(null);
277: append(p, connect);
278: }
279:
280: public void append(PathIterator path, boolean connect) {
281: while (!path.isDone()) {
282: float coords[] = new float[6];
283: switch (path.currentSegment(coords)) {
284: case PathIterator.SEG_MOVETO:
285: if (!connect || typeSize == 0) {
286: moveTo(coords[0], coords[1]);
287: break;
288: }
289: if (types[typeSize - 1] != PathIterator.SEG_CLOSE
290: && points[pointSize - 2] == coords[0]
291: && points[pointSize - 1] == coords[1]) {
292: break;
293: }
294: // NO BREAK;
295: case PathIterator.SEG_LINETO:
296: lineTo(coords[0], coords[1]);
297: break;
298: case PathIterator.SEG_QUADTO:
299: quadTo(coords[0], coords[1], coords[2], coords[3]);
300: break;
301: case PathIterator.SEG_CUBICTO:
302: curveTo(coords[0], coords[1], coords[2], coords[3],
303: coords[4], coords[5]);
304: break;
305: case PathIterator.SEG_CLOSE:
306: closePath();
307: break;
308: }
309: path.next();
310: connect = false;
311: }
312: }
313:
314: public Point2D getCurrentPoint() {
315: if (typeSize == 0) {
316: return null;
317: }
318: int j = pointSize - 2;
319: if (types[typeSize - 1] == PathIterator.SEG_CLOSE) {
320:
321: for (int i = typeSize - 2; i > 0; i--) {
322: int type = types[i];
323: if (type == PathIterator.SEG_MOVETO) {
324: break;
325: }
326: j -= pointShift[type];
327: }
328: }
329: return new Point2D.Float(points[j], points[j + 1]);
330: }
331:
332: public void reset() {
333: typeSize = 0;
334: pointSize = 0;
335: }
336:
337: public void transform(AffineTransform t) {
338: t.transform(points, 0, points, 0, pointSize / 2);
339: }
340:
341: public Shape createTransformedShape(AffineTransform t) {
342: GeneralPath p = (GeneralPath) clone();
343: if (t != null) {
344: p.transform(t);
345: }
346: return p;
347: }
348:
349: public Rectangle2D getBounds2D() {
350: float rx1, ry1, rx2, ry2;
351: if (pointSize == 0) {
352: rx1 = ry1 = rx2 = ry2 = 0.0f;
353: } else {
354: int i = pointSize - 1;
355: ry1 = ry2 = points[i--];
356: rx1 = rx2 = points[i--];
357: while (i > 0) {
358: float y = points[i--];
359: float x = points[i--];
360: if (x < rx1) {
361: rx1 = x;
362: } else if (x > rx2) {
363: rx2 = x;
364: }
365: if (y < ry1) {
366: ry1 = y;
367: } else if (y > ry2) {
368: ry2 = y;
369: }
370: }
371: }
372: return new Rectangle2D.Float(rx1, ry1, rx2 - rx1, ry2 - ry1);
373: }
374:
375: public Rectangle getBounds() {
376: return getBounds2D().getBounds();
377: }
378:
379: /**
380: * Checks cross count according to path rule to define is it point inside shape or not.
381: * @param cross - the point cross count
382: * @return true if point is inside path, or false otherwise
383: */
384: boolean isInside(int cross) {
385: if (rule == WIND_NON_ZERO) {
386: return Crossing.isInsideNonZero(cross);
387: }
388: return Crossing.isInsideEvenOdd(cross);
389: }
390:
391: public boolean contains(double px, double py) {
392: return isInside(Crossing.crossShape(this , px, py));
393: }
394:
395: public boolean contains(double rx, double ry, double rw, double rh) {
396: int cross = Crossing.intersectShape(this , rx, ry, rw, rh);
397: return cross != Crossing.CROSSING && isInside(cross);
398: }
399:
400: public boolean intersects(double rx, double ry, double rw, double rh) {
401: int cross = Crossing.intersectShape(this , rx, ry, rw, rh);
402: return cross == Crossing.CROSSING || isInside(cross);
403: }
404:
405: public boolean contains(Point2D p) {
406: return contains(p.getX(), p.getY());
407: }
408:
409: public boolean contains(Rectangle2D r) {
410: return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
411: }
412:
413: public boolean intersects(Rectangle2D r) {
414: return intersects(r.getX(), r.getY(), r.getWidth(), r
415: .getHeight());
416: }
417:
418: public PathIterator getPathIterator(AffineTransform t) {
419: return new Iterator(this , t);
420: }
421:
422: public PathIterator getPathIterator(AffineTransform t,
423: double flatness) {
424: return new FlatteningPathIterator(getPathIterator(t), flatness);
425: }
426:
427: @Override
428: public Object clone() {
429: try {
430: GeneralPath p = (GeneralPath) super .clone();
431: p.types = types.clone();
432: p.points = points.clone();
433: return p;
434: } catch (CloneNotSupportedException e) {
435: throw new InternalError();
436: }
437: }
438:
439: }
|