1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17/** 18 * @author Denis M. Kishenko 19 * @version $Revision$ 20 */ 21 22package java.awt.geom; 23 24import java.util.NoSuchElementException; 25 26import org.apache.harmony.awt.internal.nls.Messages; 27 28/** 29 * The Class Ellipse2D describes an ellipse defined by a rectangular area in 30 * which it is inscribed. 31 * 32 * @since Android 1.0 33 */ 34public abstract class Ellipse2D extends RectangularShape { 35 36 /** 37 * The Class Float is the subclass of Ellipse2D that has all of its data 38 * values stored with float-level precision. 39 * 40 * @since Android 1.0 41 */ 42 public static class Float extends Ellipse2D { 43 44 /** 45 * The x coordinate of the upper left corner of the ellipse's bounding 46 * rectangle. 47 */ 48 public float x; 49 50 /** 51 * The y coordinate of the upper left corner of the ellipse's bounding 52 * rectangle. 53 */ 54 public float y; 55 56 /** 57 * The width of the ellipse's bounding rectangle. 58 */ 59 public float width; 60 61 /** 62 * The height of the ellipse's bounding rectangle. 63 */ 64 public float height; 65 66 /** 67 * Instantiates a new float-valued Ellipse2D. 68 */ 69 public Float() { 70 } 71 72 /** 73 * Instantiates a new float-valued Ellipse2D with the specified data. 74 * 75 * @param x 76 * the x coordinate of the upper left corner of the ellipse's 77 * bounding rectangle. 78 * @param y 79 * the y coordinate of the upper left corner of the ellipse's 80 * bounding rectangle. 81 * @param width 82 * the width of the ellipse's bounding rectangle. 83 * @param height 84 * the height of the ellipse's bounding rectangle. 85 */ 86 public Float(float x, float y, float width, float height) { 87 setFrame(x, y, width, height); 88 } 89 90 @Override 91 public double getX() { 92 return x; 93 } 94 95 @Override 96 public double getY() { 97 return y; 98 } 99 100 @Override 101 public double getWidth() { 102 return width; 103 } 104 105 @Override 106 public double getHeight() { 107 return height; 108 } 109 110 @Override 111 public boolean isEmpty() { 112 return width <= 0.0 || height <= 0.0; 113 } 114 115 /** 116 * Sets the data of the ellipse's bounding rectangle. 117 * 118 * @param x 119 * the x coordinate of the upper left corner of the ellipse's 120 * bounding rectangle. 121 * @param y 122 * the y coordinate of the upper left corner of the ellipse's 123 * bounding rectangle. 124 * @param width 125 * the width of the ellipse's bounding rectangle. 126 * @param height 127 * the height of the ellipse's bounding rectangle. 128 */ 129 public void setFrame(float x, float y, float width, float height) { 130 this.x = x; 131 this.y = y; 132 this.width = width; 133 this.height = height; 134 } 135 136 @Override 137 public void setFrame(double x, double y, double width, double height) { 138 this.x = (float)x; 139 this.y = (float)y; 140 this.width = (float)width; 141 this.height = (float)height; 142 } 143 144 public Rectangle2D getBounds2D() { 145 return new Rectangle2D.Float(x, y, width, height); 146 } 147 } 148 149 /** 150 * The Class Double is the subclass of Ellipse2D that has all of its data 151 * values stored with double-level precision. 152 * 153 * @since Android 1.0 154 */ 155 public static class Double extends Ellipse2D { 156 157 /** 158 * The x coordinate of the upper left corner of the ellipse's bounding 159 * rectangle. 160 */ 161 public double x; 162 163 /** 164 * The y coordinate of the upper left corner of the ellipse's bounding 165 * rectangle. 166 */ 167 public double y; 168 169 /** 170 * The width of the ellipse's bounding rectangle. 171 */ 172 public double width; 173 174 /** 175 * The height of the ellipse's bounding rectangle. 176 */ 177 public double height; 178 179 /** 180 * Instantiates a new double-valued Ellipse2D. 181 */ 182 public Double() { 183 } 184 185 /** 186 * Instantiates a new double-valued Ellipse2D with the specified data. 187 * 188 * @param x 189 * the x coordinate of the upper left corner of the ellipse's 190 * bounding rectangle. 191 * @param y 192 * the y coordinate of the upper left corner of the ellipse's 193 * bounding rectangle. 194 * @param width 195 * the width of the ellipse's bounding rectangle. 196 * @param height 197 * the height of the ellipse's bounding rectangle. 198 */ 199 public Double(double x, double y, double width, double height) { 200 setFrame(x, y, width, height); 201 } 202 203 @Override 204 public double getX() { 205 return x; 206 } 207 208 @Override 209 public double getY() { 210 return y; 211 } 212 213 @Override 214 public double getWidth() { 215 return width; 216 } 217 218 @Override 219 public double getHeight() { 220 return height; 221 } 222 223 @Override 224 public boolean isEmpty() { 225 return width <= 0.0 || height <= 0.0; 226 } 227 228 @Override 229 public void setFrame(double x, double y, double width, double height) { 230 this.x = x; 231 this.y = y; 232 this.width = width; 233 this.height = height; 234 } 235 236 public Rectangle2D getBounds2D() { 237 return new Rectangle2D.Double(x, y, width, height); 238 } 239 } 240 241 /* 242 * Ellipse2D path iterator 243 */ 244 /** 245 * The subclass of PathIterator to traverse an Ellipse2D. 246 */ 247 class Iterator implements PathIterator { 248 249 /* 250 * Ellipse is subdivided into four quarters by x and y axis. Each part 251 * approximated by cubic Bezier curve. Arc in first quarter is started 252 * in (a, 0) and finished in (0, b) points. Control points for cubic 253 * curve wiil be (a, 0), (a, m), (n, b) and (0, b) where n and m are 254 * calculated based on requirement Bezier curve in point 0.5 should lay 255 * on the arc. 256 */ 257 258 /** 259 * The coefficient to calculate control points of Bezier curves. 260 */ 261 final double u = 2.0 / 3.0 * (Math.sqrt(2.0) - 1.0); 262 263 /** 264 * The points coordinates calculation table. 265 */ 266 final double points[][] = { 267 { 268 1.0, 0.5 + u, 0.5 + u, 1.0, 0.5, 1.0 269 }, { 270 0.5 - u, 1.0, 0.0, 0.5 + u, 0.0, 0.5 271 }, { 272 0.0, 0.5 - u, 0.5 - u, 0.0, 0.5, 0.0 273 }, { 274 0.5 + u, 0.0, 1.0, 0.5 - u, 1.0, 0.5 275 } 276 }; 277 278 /** 279 * The x coordinate of left-upper corner of the ellipse bounds. 280 */ 281 double x; 282 283 /** 284 * The y coordinate of left-upper corner of the ellipse bounds. 285 */ 286 double y; 287 288 /** 289 * The width of the ellipse bounds. 290 */ 291 double width; 292 293 /** 294 * The height of the ellipse bounds. 295 */ 296 double height; 297 298 /** 299 * The path iterator transformation. 300 */ 301 AffineTransform t; 302 303 /** 304 * The current segment index. 305 */ 306 int index; 307 308 /** 309 * Constructs a new Ellipse2D.Iterator for given ellipse and 310 * transformation 311 * 312 * @param e 313 * the source Ellipse2D object. 314 * @param t 315 * the affine transformation object. 316 */ 317 Iterator(Ellipse2D e, AffineTransform t) { 318 this.x = e.getX(); 319 this.y = e.getY(); 320 this.width = e.getWidth(); 321 this.height = e.getHeight(); 322 this.t = t; 323 if (width < 0.0 || height < 0.0) { 324 index = 6; 325 } 326 } 327 328 public int getWindingRule() { 329 return WIND_NON_ZERO; 330 } 331 332 public boolean isDone() { 333 return index > 5; 334 } 335 336 public void next() { 337 index++; 338 } 339 340 public int currentSegment(double[] coords) { 341 if (isDone()) { 342 // awt.4B=Iterator out of bounds 343 throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ 344 } 345 if (index == 5) { 346 return SEG_CLOSE; 347 } 348 int type; 349 int count; 350 if (index == 0) { 351 type = SEG_MOVETO; 352 count = 1; 353 double p[] = points[3]; 354 coords[0] = x + p[4] * width; 355 coords[1] = y + p[5] * height; 356 } else { 357 type = SEG_CUBICTO; 358 count = 3; 359 double p[] = points[index - 1]; 360 int j = 0; 361 for (int i = 0; i < 3; i++) { 362 coords[j] = x + p[j++] * width; 363 coords[j] = y + p[j++] * height; 364 } 365 } 366 if (t != null) { 367 t.transform(coords, 0, coords, 0, count); 368 } 369 return type; 370 } 371 372 public int currentSegment(float[] coords) { 373 if (isDone()) { 374 // awt.4B=Iterator out of bounds 375 throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$ 376 } 377 if (index == 5) { 378 return SEG_CLOSE; 379 } 380 int type; 381 int count; 382 if (index == 0) { 383 type = SEG_MOVETO; 384 count = 1; 385 double p[] = points[3]; 386 coords[0] = (float)(x + p[4] * width); 387 coords[1] = (float)(y + p[5] * height); 388 } else { 389 type = SEG_CUBICTO; 390 count = 3; 391 int j = 0; 392 double p[] = points[index - 1]; 393 for (int i = 0; i < 3; i++) { 394 coords[j] = (float)(x + p[j++] * width); 395 coords[j] = (float)(y + p[j++] * height); 396 } 397 } 398 if (t != null) { 399 t.transform(coords, 0, coords, 0, count); 400 } 401 return type; 402 } 403 404 } 405 406 /** 407 * Instantiates a new Ellipse2D. 408 */ 409 protected Ellipse2D() { 410 } 411 412 public boolean contains(double px, double py) { 413 if (isEmpty()) { 414 return false; 415 } 416 417 double a = (px - getX()) / getWidth() - 0.5; 418 double b = (py - getY()) / getHeight() - 0.5; 419 420 return a * a + b * b < 0.25; 421 } 422 423 public boolean intersects(double rx, double ry, double rw, double rh) { 424 if (isEmpty() || rw <= 0.0 || rh <= 0.0) { 425 return false; 426 } 427 428 double cx = getX() + getWidth() / 2.0; 429 double cy = getY() + getHeight() / 2.0; 430 431 double rx1 = rx; 432 double ry1 = ry; 433 double rx2 = rx + rw; 434 double ry2 = ry + rh; 435 436 double nx = cx < rx1 ? rx1 : (cx > rx2 ? rx2 : cx); 437 double ny = cy < ry1 ? ry1 : (cy > ry2 ? ry2 : cy); 438 439 return contains(nx, ny); 440 } 441 442 public boolean contains(double rx, double ry, double rw, double rh) { 443 if (isEmpty() || rw <= 0.0 || rh <= 0.0) { 444 return false; 445 } 446 447 double rx1 = rx; 448 double ry1 = ry; 449 double rx2 = rx + rw; 450 double ry2 = ry + rh; 451 452 return contains(rx1, ry1) && contains(rx2, ry1) && contains(rx2, ry2) && contains(rx1, ry2); 453 } 454 455 public PathIterator getPathIterator(AffineTransform at) { 456 return new Iterator(this, at); 457 } 458} 459