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.util.NoSuchElementException;
023:
024: import org.apache.harmony.awt.internal.nls.Messages;
025:
026: public abstract class RoundRectangle2D extends RectangularShape {
027:
028: public static class Float extends RoundRectangle2D {
029:
030: public float x;
031: public float y;
032: public float width;
033: public float height;
034: public float arcwidth;
035: public float archeight;
036:
037: public Float() {
038: }
039:
040: public Float(float x, float y, float width, float height,
041: float arcwidth, float archeight) {
042: setRoundRect(x, y, width, height, arcwidth, archeight);
043: }
044:
045: @Override
046: public double getX() {
047: return x;
048: }
049:
050: @Override
051: public double getY() {
052: return y;
053: }
054:
055: @Override
056: public double getWidth() {
057: return width;
058: }
059:
060: @Override
061: public double getHeight() {
062: return height;
063: }
064:
065: @Override
066: public double getArcWidth() {
067: return arcwidth;
068: }
069:
070: @Override
071: public double getArcHeight() {
072: return archeight;
073: }
074:
075: @Override
076: public boolean isEmpty() {
077: return width <= 0.0f || height <= 0.0f;
078: }
079:
080: public void setRoundRect(float x, float y, float width,
081: float height, float arcwidth, float archeight) {
082: this .x = x;
083: this .y = y;
084: this .width = width;
085: this .height = height;
086: this .arcwidth = arcwidth;
087: this .archeight = archeight;
088: }
089:
090: @Override
091: public void setRoundRect(double x, double y, double width,
092: double height, double arcwidth, double archeight) {
093: this .x = (float) x;
094: this .y = (float) y;
095: this .width = (float) width;
096: this .height = (float) height;
097: this .arcwidth = (float) arcwidth;
098: this .archeight = (float) archeight;
099: }
100:
101: @Override
102: public void setRoundRect(RoundRectangle2D rr) {
103: this .x = (float) rr.getX();
104: this .y = (float) rr.getY();
105: this .width = (float) rr.getWidth();
106: this .height = (float) rr.getHeight();
107: this .arcwidth = (float) rr.getArcWidth();
108: this .archeight = (float) rr.getArcHeight();
109: }
110:
111: public Rectangle2D getBounds2D() {
112: return new Rectangle2D.Float(x, y, width, height);
113: }
114: }
115:
116: public static class Double extends RoundRectangle2D {
117:
118: public double x;
119: public double y;
120: public double width;
121: public double height;
122: public double arcwidth;
123: public double archeight;
124:
125: public Double() {
126: }
127:
128: public Double(double x, double y, double width, double height,
129: double arcwidth, double archeight) {
130: setRoundRect(x, y, width, height, arcwidth, archeight);
131: }
132:
133: @Override
134: public double getX() {
135: return x;
136: }
137:
138: @Override
139: public double getY() {
140: return y;
141: }
142:
143: @Override
144: public double getWidth() {
145: return width;
146: }
147:
148: @Override
149: public double getHeight() {
150: return height;
151: }
152:
153: @Override
154: public double getArcWidth() {
155: return arcwidth;
156: }
157:
158: @Override
159: public double getArcHeight() {
160: return archeight;
161: }
162:
163: @Override
164: public boolean isEmpty() {
165: return width <= 0.0 || height <= 0.0;
166: }
167:
168: @Override
169: public void setRoundRect(double x, double y, double width,
170: double height, double arcwidth, double archeight) {
171: this .x = x;
172: this .y = y;
173: this .width = width;
174: this .height = height;
175: this .arcwidth = arcwidth;
176: this .archeight = archeight;
177: }
178:
179: @Override
180: public void setRoundRect(RoundRectangle2D rr) {
181: this .x = rr.getX();
182: this .y = rr.getY();
183: this .width = rr.getWidth();
184: this .height = rr.getHeight();
185: this .arcwidth = rr.getArcWidth();
186: this .archeight = rr.getArcHeight();
187: }
188:
189: public Rectangle2D getBounds2D() {
190: return new Rectangle2D.Double(x, y, width, height);
191: }
192: }
193:
194: /*
195: * RoundRectangle2D path iterator
196: */
197: class Iterator implements PathIterator {
198:
199: /*
200: * Path for round corners generated the same way as Ellipse2D
201: */
202:
203: /**
204: * The coefficient to calculate control points of Bezier curves
205: */
206: double u = 0.5 - 2.0 / 3.0 * (Math.sqrt(2.0) - 1.0);
207:
208: /**
209: * The points coordinates calculation table.
210: */
211: double points[][] = {
212: { 0.0, 0.5, 0.0, 0.0 }, // MOVETO
213: { 1.0, -0.5, 0.0, 0.0 }, // LINETO
214: { 1.0, -u, 0.0, 0.0, // CUBICTO
215: 1.0, 0.0, 0.0, u, 1.0, 0.0, 0.0, 0.5 },
216: { 1.0, 0.0, 1.0, -0.5 }, // LINETO
217: { 1.0, 0.0, 1.0, -u, // CUBICTO
218: 1.0, -u, 1.0, 0.0, 1.0, -0.5, 1.0, 0.0 },
219: { 0.0, 0.5, 1.0, 0.0 }, // LINETO
220: { 0.0, u, 1.0, 0.0, // CUBICTO
221: 0.0, 0.0, 1.0, -u, 0.0, 0.0, 1.0, -0.5 },
222: { 0.0, 0.0, 0.0, 0.5 }, // LINETO
223: { 0.0, 0.0, 0.0, u, // CUBICTO
224: 0.0, u, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0 } };
225:
226: /**
227: * The segment types correspond to points array
228: */
229: int types[] = { SEG_MOVETO, SEG_LINETO, SEG_CUBICTO,
230: SEG_LINETO, SEG_CUBICTO, SEG_LINETO, SEG_CUBICTO,
231: SEG_LINETO, SEG_CUBICTO };
232:
233: /**
234: * The x coordinate of left-upper corner of the round rectangle bounds
235: */
236: double x;
237:
238: /**
239: * The y coordinate of left-upper corner of the round rectangle bounds
240: */
241: double y;
242:
243: /**
244: * The width of the round rectangle bounds
245: */
246: double width;
247:
248: /**
249: * The height of the round rectangle bounds
250: */
251: double height;
252:
253: /**
254: * The width of arc corners of the round rectangle
255: */
256: double aw;
257:
258: /**
259: * The height of arc corners of the round rectangle
260: */
261: double ah;
262:
263: /**
264: * The path iterator transformation
265: */
266: AffineTransform t;
267:
268: /**
269: * The current segmenet index
270: */
271: int index;
272:
273: /**
274: * Constructs a new RoundRectangle2D.Iterator for given round rectangle and transformation.
275: * @param rr - the source RoundRectangle2D object
276: * @param at - the AffineTransform object to apply rectangle path
277: */
278: Iterator(RoundRectangle2D rr, AffineTransform at) {
279: this .x = rr.getX();
280: this .y = rr.getY();
281: this .width = rr.getWidth();
282: this .height = rr.getHeight();
283: this .aw = Math.min(width, rr.getArcWidth());
284: this .ah = Math.min(height, rr.getArcHeight());
285: this .t = at;
286: if (width < 0.0 || height < 0.0 || aw < 0.0 || ah < 0.0) {
287: index = points.length;
288: }
289: }
290:
291: public int getWindingRule() {
292: return WIND_NON_ZERO;
293: }
294:
295: public boolean isDone() {
296: return index > points.length;
297: }
298:
299: public void next() {
300: index++;
301: }
302:
303: public int currentSegment(double[] coords) {
304: if (isDone()) {
305: // awt.4B=Iterator out of bounds
306: throw new NoSuchElementException(Messages
307: .getString("awt.4B")); //$NON-NLS-1$
308: }
309: if (index == points.length) {
310: return SEG_CLOSE;
311: }
312: int j = 0;
313: double p[] = points[index];
314: for (int i = 0; i < p.length; i += 4) {
315: coords[j++] = x + p[i + 0] * width + p[i + 1] * aw;
316: coords[j++] = y + p[i + 2] * height + p[i + 3] * ah;
317: }
318: if (t != null) {
319: t.transform(coords, 0, coords, 0, j / 2);
320: }
321: return types[index];
322: }
323:
324: public int currentSegment(float[] coords) {
325: if (isDone()) {
326: // awt.4B=Iterator out of bounds
327: throw new NoSuchElementException(Messages
328: .getString("awt.4B")); //$NON-NLS-1$
329: }
330: if (index == points.length) {
331: return SEG_CLOSE;
332: }
333: int j = 0;
334: double p[] = points[index];
335: for (int i = 0; i < p.length; i += 4) {
336: coords[j++] = (float) (x + p[i + 0] * width + p[i + 1]
337: * aw);
338: coords[j++] = (float) (y + p[i + 2] * height + p[i + 3]
339: * ah);
340: }
341: if (t != null) {
342: t.transform(coords, 0, coords, 0, j / 2);
343: }
344: return types[index];
345: }
346:
347: }
348:
349: protected RoundRectangle2D() {
350: }
351:
352: public abstract double getArcWidth();
353:
354: public abstract double getArcHeight();
355:
356: public abstract void setRoundRect(double x, double y, double width,
357: double height, double arcWidth, double arcHeight);
358:
359: public void setRoundRect(RoundRectangle2D rr) {
360: setRoundRect(rr.getX(), rr.getY(), rr.getWidth(), rr
361: .getHeight(), rr.getArcWidth(), rr.getArcHeight());
362: }
363:
364: @Override
365: public void setFrame(double x, double y, double width, double height) {
366: setRoundRect(x, y, width, height, getArcWidth(), getArcHeight());
367: }
368:
369: public boolean contains(double px, double py) {
370: if (isEmpty()) {
371: return false;
372: }
373:
374: double rx1 = getX();
375: double ry1 = getY();
376: double rx2 = rx1 + getWidth();
377: double ry2 = ry1 + getHeight();
378:
379: if (px < rx1 || px >= rx2 || py < ry1 || py >= ry2) {
380: return false;
381: }
382:
383: double aw = getArcWidth() / 2.0;
384: double ah = getArcHeight() / 2.0;
385:
386: double cx, cy;
387:
388: if (px < rx1 + aw) {
389: cx = rx1 + aw;
390: } else if (px > rx2 - aw) {
391: cx = rx2 - aw;
392: } else {
393: return true;
394: }
395:
396: if (py < ry1 + ah) {
397: cy = ry1 + ah;
398: } else if (py > ry2 - ah) {
399: cy = ry2 - ah;
400: } else {
401: return true;
402: }
403:
404: px = (px - cx) / aw;
405: py = (py - cy) / ah;
406: return px * px + py * py <= 1.0;
407: }
408:
409: public boolean intersects(double rx, double ry, double rw, double rh) {
410: if (isEmpty() || rw <= 0.0 || rh <= 0.0) {
411: return false;
412: }
413:
414: double x1 = getX();
415: double y1 = getY();
416: double x2 = x1 + getWidth();
417: double y2 = y1 + getHeight();
418:
419: double rx1 = rx;
420: double ry1 = ry;
421: double rx2 = rx + rw;
422: double ry2 = ry + rh;
423:
424: if (rx2 < x1 || x2 < rx1 || ry2 < y1 || y2 < ry1) {
425: return false;
426: }
427:
428: double cx = (x1 + x2) / 2.0;
429: double cy = (y1 + y2) / 2.0;
430:
431: double nx = cx < rx1 ? rx1 : (cx > rx2 ? rx2 : cx);
432: double ny = cy < ry1 ? ry1 : (cy > ry2 ? ry2 : cy);
433:
434: return contains(nx, ny);
435: }
436:
437: public boolean contains(double rx, double ry, double rw, double rh) {
438: if (isEmpty() || rw <= 0.0 || rh <= 0.0) {
439: return false;
440: }
441:
442: double rx1 = rx;
443: double ry1 = ry;
444: double rx2 = rx + rw;
445: double ry2 = ry + rh;
446:
447: return contains(rx1, ry1) && contains(rx2, ry1)
448: && contains(rx2, ry2) && contains(rx1, ry2);
449: }
450:
451: public PathIterator getPathIterator(AffineTransform at) {
452: return new Iterator(this, at);
453: }
454:
455: }
|